Content from Navigation

Last updated on 2022-04-27 | Edit this page



  • What is GitLab?
  • How can I find my way around GitLab?


  • State the primary entity of organization.
  • Use GitLab’s interface to find a project.
  • State one purpose of groups.
  • Use GitLab’s interface to find group.


GitLab is a web application for managing software projects. However, the features it provides can also be used for related purposes; for example, issue or task management, document management or project management in general.

GitLab is open source software and can be run by anyone on their own hardware. The company behind the software runs one so called instance under where anyone (resident in a country or territory not sanction by the USA) can create an account and use it within certain limits for free. But there are also instances run by diverse people and institutions from hobbyists over small NGOs to large organizations (e.g. Those are not necessarily publicly accessible.

GitHub is a direct competitor to, and probably inspiration for, GitLab. Unlike GitLab, GitHub is not open source software, but large customers can run their own instances. With respect to the content covered by this lesson they are very similar and only differ in details. So, if you are already familiar with using GitHub, all this lesson can probably teach you is how concepts already familiar to you from GitHub are called in GitLab.

Finding Projects

Log in to GitLab

Navigate to to’s sign in page and log in to your account. Once logged in, have a look at what projects you can see in the first three tabs (“Your projects”, “Starred projects”, and “Explore projects”). Play around with the filters and sort settings.

This exercise should take about 5 minutes.

When we log into GitLab for the first time, we are greeted by a welcome page, offering us four ways to move forward:

  • “Create a project”
  • “Create a group”
  • “Explore public projects”
  • “Learn more about GitLab”

We will cover how to create a project and a group in the next episode. For now, we want to learn how to navigate GitLab. For that we select “Explore public projects”. This leads us to the projects overview page. The project is the main organizational unit of GitLab.


The welcome page is only shown as long as we are not a member of any project yet. On instances other than on it might not be shown at all. In the case the configured home page is shown.

The project overview page is the default homepage. However, the homepage is a configuration option for GitLab instances as well as users. You may have changed that setting and on other instances an administrator might have done so.

You can always navigate to the project overview page by selecting one of the subentries of the “Projects” option in the menu accessible from the menu button on the left side of the top menu bar.

Depending on whether you have used GitLab before or have been assigned to a group by someone else you may see some or no projects on the first two tabs, “Your projects” and “Starred projects”. But on GitLab’s own instance you will certainly see projects on the “Explore projects” tab. For example, while writing the lesson material the tab looked like this:

Project overview page with tab “Explore projects” and subtab “Most stars” open. The tab shows a list of projects with icon, name, descriptions, and four statistical values for each entry.
Project overview page

The first tab, “Your projects”, lists all projects that you are a member of. When you create a project you automatically become a member and others can add you as a member to a project. We will learn about another way to become a member of a project in the episode on groups.

The second tab, “Starred projects”, lists projects that you have “starred”. In this context “starring” it is similar to bookmarks in a browser. Every project homepage shows a widget in the upper right corner labeled “Star”. By pressing it you “star” or bookmark the project.

The third tab, “Explore projects”, lists all projects that you can access. This includes all projects from the “Your projects” tab as well as all projects that are publicly visible.


GitLab offers three settings for the visibility of a project: public, internal, and private. Publicly visible projects can be looked at by anyone that can access the GitLab instance, projects with internal visibility can be looked at by anyone logged in to the instance, while projects with private visibility can only be locked at by its members.

On the visibility “internal” is disabled. Everyone can create an account, log in to the instance, and thus could look at any project of internal visibility anyway.

Self-hosted instances might also disable some visibility types. For example, the public visibility might be disabled, to prevent users from publishing something to the whole internet.

When working with GitLab, most of the time you will want to access projects you are already a member of (in the GitLab sense). In that case the list on the “Your projects” will be all you need to find the project you are looking for. When the list becomes long, the search field next to the tabs allows you to filter the projects by name, just as the placeholder text suggests.

You can also use that search field on the “Explore projects” tab to search through all projects that you can look at.

The search field in the top menu bar can also be used to find projects. It shows other search results, too, but clearly labels what is what.

Finally, you can use the search field under the “Projects” in the menu accessible from the menu button on the left side of the top menu bar to search for projects.

Finding a project

Use the search methods GitLab provides to find the project page for the software “Inkscape”.

This exercise should take about 5 minutes.

The project page is located at If you found yourself at you found the group page of the Inkscape community.

Finding Groups

The second organizational entity, next to projects, are groups. Groups can contain projects and other groups. So groups can be used to give structure to the organization of multiple projects. In a later episode, when we talk about members of groups and projects, we learn about another use for groups.

Groups and projects work somewhat similar to directories and files in the file system. Every group and every project can be contained in only one group and a group cannot contain itself.

To make our lives easier later on, we introduce to terms regarding a group’s position ins this system:

  • A group not contained in another group is called top-level group.
  • A group contain in another group is called a subgroup of that group.

Top-level groups and Subgroup

On the group page identify the top-level group’s name and one subgroup or contained project of that group.

Hint: Note that groups cannot be starred.

This exercise should take about 5 minutes.

  • The top-level groups name is Inkscape as it is written at the top of the main content.
  • Any entry in the list “Subgroups and projects” is a subgroup of contained project.

Similar as for projects, GitLab provides an overview page for groups. You can navigate there by selecting “Groups” and then “Your groups” or “Explore groups” in the menu accessible from the menu button on the left side of the top menu bar.

The two tabs on that page, “Your groups” and “Explore public groups” work analogously to their corresponding tabs on the project overview page discussed above.

For finding groups there are analogous search options as mentioned for projects before.


  • Projects are GitLab’s primary entity of organization.
  • Projects you have access to are listed in the “Your projects” tab on the project overview page.
  • The project overview page is GitLab’s homepage unless configured differently.
  • You can search for projects in the top menu, the top search bar and on the “Explore projects” tab of project overview page.
  • A project’s visibility can be set to either private, internal, or public.
  • On some instances a visibility class is disabled.
  • Groups can contain projects and other groups.
  • A top-level group is a group not contained in another.
  • A subgroup is a group contained in another.
  • Groups you have access to are listed in the “Your groups” tab on the group overview page.
  • You can search for groups in the top menu, the top search bar and on the “Explore groups” tab of group overview page.

Content from GitLab Projects

Last updated on 2022-05-06 | Edit this page



  • How can I use GitLab to manage my project’s files?


  • Create a project.
  • Change name, description, visibility, and avatar of a project.
  • Push a local Git repository to a newly created project.
  • Archive (or delete) a project.

Creating a Project

After knowing how to find groups and projects. We want to go from passively browsing GitLab to actively using it. We are going to create a project and connect a local Git repository to it.

To create a project, click on the button with the “+” symbol in the top menu bar and select “New project/repository”.

Multiple options are presented for how to create the new project. In this lesson we will only look at, and use, the first option: “Create blank project”. So click on that.

This leads to the following page:

Create blank project form with text input fields labeled “Project name”, “Project URL”, “Project slug”, “Project description (optional)”, “Project deployment target (optional)”, a radio button element labeled “Visibility Level” with options “Private” and “Public”, and two checkboxes labeled “Initialize repository with a README” and “Enable Static Application Security Testing (SAST)”.
Create blank project form

The “Project name” field is for just that, the project name. Its value has no other purpose and can be changed at anytime without indirect consequences (the direct consequence being, that its name will be different). We will call it “Research Diary”.

As we fill in the project name, a project slug gets suggested in the respective field. The project slug is the last part of the project’s, and the associated Git repository’s, URL or web address.

If the user belongs to at least one group, the URL’s middle part can be chosen in the drop-down field labeled “Project URL”, which defaults to the current user’s username. This is the location for a user’s project (and groups). Other options in the drop-down list are the groups in which the current user may create projects.

Project URL and Project Slug

The two fields under labels “Project URL” and “Project slug” are the only fields in this form for which changing the value later might cause problems. Since they determine the URL under which the project’s pages and the project’s Git repository can be found, changing them later might brake links and bookmarks as well as connections from Git repositories on other systems, for example on contributors’ machines.

The project description appears in many lists and on some pages under the project’s name. We add a concise description: “My research diary, a collection of Markdown files”


Markdown is a markup language, like HTML on which the World Wide Web is based or wikitext used to write Wikipedia’s content. Its markup directives, indicating for example that something is a headline or a list item, are such that they serve their purpose even in the plain text form.

There exist many variants (or flavors) of Markdown and GitLab has its own. Other than for rendering Markdown files in repositories on its web interface, GitLab allows for Markdown input in many of its interface’s text fields, such as issue descriptions or comments.

This will be pointed out as we get to use these fields during the lesson.

We ignore the field labeled “Project deployment target (optional)”.

The choice of radio button under label “Visibility Level” determines the project’s visibility. As mentioned in the previous episode “Finding Projects and Groups”, the visibility “Internal” is disabled on, so only “Private”, the default, and “Public” are available. We choose “Private”.

If, as per default, the checkbox “Initialize repository with a README” is checked, the project’s repository will be initialized with a commit that adds a file called Otherwise, a the project will start with an empty repository. We will be adding such a file later ourselves, so we uncheck the box.


A project’s README file usually contains basic information about the project: what it contains, how it can be used (for example build or installed, if it is a software project), how to contribute, how to get help, and licensing information.

It is common to write README files in Markdown format, indicated by the filename suffix .md.

Platforms like GitLab show the contents of a project’s README file on its homepage; if it is in Markdown format, in its rendered form.

We will ignore any other fields that may be visible depending on the GitLab instances configuration.

After clicking the “Create project” button, GitLab creates the project and redirects us to the project’s homepage, which looks similar to this:

Project homepage for a new project. On the left, a menu leading to other project pages: Project Information, Issues, Merge requests, CI/CD, Security & Compliance, Deployments, Monitor, Infrastructure, Packages & Registries, Analytics, Wiki, Snippets, and Settings. On the right, taking up the major part of the width, the project page: Starting at the top, the project’s name “Research Diary“, followed by its descriptions “My research diary, a collection of Markdown files”, an encouragement to “Invite your team” with a button “Invite members”, the statement that “The repository for this project is empty” with various buttons for getting initial content into the repository. At the bottom, the beginning of instructions on how to push an existing repository are visible, but cut off.
Fresh project homepage

The page is split into the menu on the left and the project overview on the right.

The menu leads to pages related to various properties, processes, and content of the project. It is visible on each of these pages. The number of menu entries may seem overwhelming, in particular when one notices that the entries have subentries. However, it is not necessary to know what hides behind all these entries to use GitLab. Following this lesson, we will get to know parts of what lies behind the entries “Project Information”, “Issues”, “Merge requests”, “CI/CD”, “Wiki”, and “Settings”.

The project overview shows (from the top): The project’s avatar (or icon), name and description; a prompt to invite team members (we will cover members later on); a statement that our repository is currently empty with buttons for several options to add content to it; and finally the beginning of the instructions on how to push a local repository to this project’s repository to fill it with content. We will follow them in just a bit.

The project overview page will look slightly different, once we have content in its repository. We will have another look at the page then.

Change Name, Description, Visibility, or Avatar

Click on the “Settings” menu entry and change any of the project’s name, description, visibility, and avatar. Do not forget to click the “Save changes” button once you are done.

This exercise should take about 5 minutes.

The project overview page presents us with many options to add content to the project’s repository directly in GitLab. However, we will fill the project from a local Git repository. To do that, we need a local Git repository with some content.

Configuring Git

Before we initialize a local Git repository, we make sure that Git is properly configured.

To check for our name and email address, We run

$ git config --global --get

{: .language-bash}


$ git config --global --get

{: .language-bash}

If both return sensible values, we are good to go. Otherwise we provide the missing values, for example:

$ git config --global "Some Novice"
$ git config --global ""

{: .language-bash}

It is now common to name the default branch main, rather than master as is still the default in Git. For more information see, for example, the announcement of GitLab to change the default branch Since the discussion on this topic has started, Git has introduced a configuration value for the initial branch created by the git init command. We will use this, to make sure that all future repositories created locally start with a branch called main:

$ git config --global init.defaultBranch main

Creating a Local Repository

Markdown Headlines

In Markdown, headlines are marked by one up to six # characters followed by a space character in front of the text. The number of # characters indicates the heading’s level, with 1 being the most significant.

For example, a level 3 headline:

### Interesting Section Title

Research Diary’s Initial Content (Required)

Create a Git repository anywhere on your computer. Add a README file in markdown format that describes the repository’s future contents.

Remember to use a good commit message for your commit.

This exercise should take about 10 minutes.

We start by creating and navigating to a new directory, research-diary. There we initialize a git repository.

$ mkdir research-diary
$ cd research-diary
$ git init

Next we create a file called with the following content using our preferred text editor.

# Research Diary

This repository contains our research diary.
Each day gets its own file.
We use the Markdown format for our files.

Finally, we stage the added file using and create a commit.

$ git add
$ git commit -m "Add"

Checking the status of the git repository, we should see that it exists and that there are no changes in the working directory or in the stating area:

$ git status

{: .language-bash}

On branch main
nothing to commit, working tree clean

{: .output}

There is one file,

$ ls

{: .language-bash}

{: .output}

Connecting Local and Remote Repository

Having thus created our local repository, we want to save it to our repository in GitLab. To that end, we have a look at the project overview page again; this time at the instructions toward the bottom of the page. They read:

Git global setup

git config --global "Some Novice"
git config --global ""

Create a new repository

git clone
cd test
git switch -c main
git add
git commit -m "add README"
git push -u origin main

Push an existing folder

cd existing_folder
git init --initial-branch=main
git remote add origin
git add .
git commit -m "Initial commit"
git push -u origin main

Push an existing Git repository

cd existing_repo
git remote rename origin old-origin
git remote add origin
git push -u origin --all
git push -u origin --tags

We already covered the global setup.

The next three sets of instructions are mutually exclusive. The first is not for us, because we already created a repository. Neither is the second, because although we have a directory with contents, its already a Git repository. That leaves us with the third option, which is the one applicable to our situation: “Push an existing Git repository.”

Transferring our changes to a remote repository is called pushing, because we are sending the changes from us, the local repository, to somewhere else, a remote repository. Later on we will “pull” changes from a remote repository, completing the analogy.

Going back to the shell, we configure our project’s repository as a remote in our local repository:

$ git remote add origin

{: .language-bash}

https:// vs git@

We assume throughout this lesson that the user has no SSH key is configured, because we do not want to make the setup of such a key a prerequisite.

If however, a user has configured an SSH key for their account, GitLab would default to showing links to repositories in the form for Git over SSH instead.

For example, without a configured key GitLab shows

for our example repository, whereas with a configured key it would show

The HTTPS link still works if a SSH key is configured, it is only no longer shown by default.

We instruct Git to add a remote called origin with the last part as the address. Make sure to copy that last part from the project overview page, because it represents the Git address of your project’s repository on GitLab. It will be different for everyone.

Note also, that we skipped the second line of the instructions. It renames an existing remote origin to old-origin, but we have no existing remote.

Names for remotes

Apart from following restrictions that Git puts on remote names, which can be avoided without much thought by using only letters, numbers, underscores, and hyphens, we should choose meaningful names for remotes, in particular, when we add multiple remotes to a local repository.

In most examples, origin is used as the name for a remote, because it is the name of the remote created when cloning a repository. It is a meaningful name for the main remote repository, if there is one. This together with its ubiquitous use make it a very good name.

In other cases, we might call a remote to a private copy of the main repository after our username or server it is located on, for example uni for a university GitLab instance.

Finally, we push our changes to the remote repository. The command will prompt for our user name and password:

$ git push -u origin --all

{: .language-bash}

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 250 bytes | 250.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

{: .output}

Having successfully pushed our commit to the GitLab repository, we change to our browser and navigate to the project (or reload it, if we still have it open). We will be greeted by the project homepage, similar to the following screenshot:

Project homepage of a project. On the left, a menu leading to other project pages. On the right, taking up most of the width, the project homepage. Starting from the top, we first have the project title, Research Diary, followed by a line of statistical values (1 Commit, 1 Branch, 0 Tags, 72KB files, 72 KB Storage). Under these we see the description: “My research diary, a collection of Markdown files”. In the next line we see a drop-down field for branches showing “main”, the project slug¸“test”, a drop-down button labeled “+”, and buttons labeled “History”, “Find file”, “Web IDE”, a download symbol, and ”Clone”. This is followed by a box giving information on the last commit of the current branch: the commit message “Add”, the authors name and how far in the past the commit was authored, as well as prefix of the commit’s hash with a copy-to-clipboard button. This is followed by some buttons that allow to add or upload new files or setup some of GitLab features for the project. Next is a list of files, in this case only ``, with information when it was last updated and through which commit. Finally, we see a rendering of ``.
Project homepage after first push

Let us look at what has changed compared to its initial state. The interface elements encouraging us to add members to the project are gone. Of course, we can still navigate to the project’s Members later on, if we need to. We also, correctly, no longer get told that the repository is empty and the instructions on how to push content to the repository have vanished.

Instead, we see some statistics below the project’s title: 1 Commit, 1 Branch, 0 Tags, 72 KB Files, 72 KB Storage. They double as buttons other project pages.

Below the description is a new button bar, followed by a box presenting information on last commit of the currently select branch.

The buttons below, that we saw earlier, allow us to add specific files or navigate to the configuration of certain GitLab features. Note that one button changed from reading “Add README” to just “README”, since we added The button is now a link to the latest version of that file in the current branch.

Then we see a list of files. So far, there is only The list also tells us through which commit it was last changed and when that commit was created.

Finally, we see the markdown file rendered. Because having a README-file in the root directory of a repository is a widespread convention, GitLab (and other platforms like it) usually render it on a project’s homepage.

Archiving a Project

We just went through the beginning of a GitLab project’s life cycle. At its end, if it has one, a project gets archived (or deleted). We will now go through the process of archiving a project, without completing the step.

Using the menu on the left, we navigate to the projects settings.

At the bottom the page, we find a section named “Advanced”. We hit the “Expand” button right next to it and scroll down the page.

Notice that many of buttons here are not the usual blue or white, but after scrolling a little they are red. This indicates that we should be careful about clicking them. Things might break or get deleted.

Scrolling back up, we find a section labeled “Archive Project”. Clicking the button will not delete the project. Instead it will be placed in a read-only mode. Everything will be preserved but nothing can be changed anymore. In addition, the project no longer shows up in search results and on the page for exploring projects.

Most of the time archiving a project is preferable to deleting it, in particular when it comes to research projects. Do not archive the project now! We will work with it throughout this lesson.

At the bottom of the page is also a section for deleting a project, in case you ever think that is the right thing to do.


  • Creating a GitLab project requires not more than a few clicks and providing a name.
  • Changing the name of description of a GitLab project after creation has no indirect consequences.
  • You can fill a fresh GitLab project’s repository by pushing a local repository.
  • You can archive a project (and you can delete a project, but often should not.)

Content from Collaboration

Last updated on 2022-04-27 | Edit this page



  • How can multiple people collaborate on a project?


  • Explain the concepts of members and roles.
  • Add a member to a project.
  • Contribute to a project that you are a member of.

Adding Project Members

So far, each of you has created a GitLab project that no one but you can contribute to; depending on the visibility setting, no one but you might be able to even see it.

Git and GitLab can be and is used for one-person projects. But we want our colleagues to contribute to our research diary, when they keep tabs on our experiments for us on the weekends. To achieve this, we will grant others access to our GitLab project.

Using the menu on the left side of the project homepage (or nearly any other project page), we navigate to the project members page hovering over or clicking on “Project Information” and then clicking on “Members” in the submenu. The project member page should look similar to the following screenshot:

Webpage coutout titled “Project Members” with three buttons next to the title: “Import from a project”, “Invite a group”, and “Invite members”. Below a table listing the project’s members, showing account holder‘s name with account name, the membership source, when access was granted, the member’s maximum role and the membership expiration date. The table contains one column: ”Philipp Matthias Schäfer, @fiveop, Direct member, 2 weeks ago by himself, Maintainer, and no Expiration date.
Project members page

On the page we can see the page title, “Project members”, three buttons to the left of the title, and a filterable table of members, currently only listing ourselves.

The table shows our full name and account name, why we are a member of this project, when we got access—at the moment we created the project—what our maximum role is—more on that in a bit—and a disabled membership expiration date widget.

For practice we will all add an instructor to our project and remove them again right away. Click the button labeled “Invite members”, type in the username your instructors provided you with into the search field, make sure that “Guest” is selected as a role, and click the button labeled “Invite”.

After reloading the page, your instructor should now be listed next to you in the table. Unlike in your row, you can change the role and the expiration date of this new entry. There also is a new, red button in this row labeled “Remove member”.

The role determines what the member is allowed to do in the project. A maintainer has full rights, a guest almost none. GitLab’s handbook gives a detailed overview of the different roles’ permissions.

Now, we are going to remove the instructor from your project’s members again. Click the button labeled “Remove member” in the instructor’s row then click the button of the same name in the popup dialog. The page reloads itself and the entry vanishes from the table.

Adding Members

Get into pairs and add each other with the role “Developer” to your projects.

The “Developer” role grants just enough permissions for all the collaborative tasks we will practice in this lesson.

This exercise should take about 5 minutes.

Contribute Changes to Project Repository

Now, everyone should be the member of one of your co-learners’ projects. We will now add an diary entry for imaginary work that we did on their experiments last weekend.

First, we clone their repositories into the directory rd-colleague.

$ git clone rd-colleague
Cloning into 'research-diary'...
Username for '': somenovice
Password for '':
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

Moving into the directory

$ cd rd-colleague

we check its contents. If your colleague did everything we did, it should contain a single file,

$ ls

Now we are sure that everything is in order with the cloned repository and we can add our lab entry.

First we create a file, named after the date on which we did the work, listing the work we did:

* Injected transformational fluid into moss sample.
* Moss turned into several butterflys. SUCCESS!

Then we add the file as part of a new commit to the repository:

$ git add
$ git commit -m "Record work on experiment A-13"
[main 4eac24c] Record work on experiment A-13
 1 file changed, 2 insertions(+)
 create mode 100644

Markdown Lists

Lists can be represented in Markdown by starting their items with a * character followed by a space. If an list item wraps over multiple lines, lines following the first are started with sufficient spaces to align the lines text with the first line’s text.

For example:

* First item
* Second item with enough text to make us wrap to a second

We check Git’s log for our commit:

$ git log -n 1
commit 4eac24cafbcebc4f0d528bd2e1246a4624265085 (HEAD -> main)
Author: Some Novice <>
Date:   Thu Mar 17 16:00:23 2022 +0000

    Record work on experiment A-13

And everything seems to be in order, so we push our change.

$ git push
Username for '': somenovice
Password for '':
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 388 bytes | 388.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
   2dd2c21..4eac24c  main -> main

We have now recorded in pairs lab work we carried out for a colleague during the last weekend. Curious what our colleagues did for us, we navigate back to the directory of our own repository in the shell, for example:

$ cd ../research-diary

There, we pull the changes that our colleagues previously pushed.

$ git pull
Username for '': somenovice
Password for '':
Updating 2dd2c21..4eac24c
Fast-forward | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644

From the output’s last line, we can already see that a single file, was added. Letting the shell write out the files contents shows us, what our colleague did:

$ cat
* Injected transformational fluid into moss sample.
* Moss turned into several butterflys. SUCCESS!

We successfully collaborated through GitLab on a project of text files.


  • Adding others as members allows them to directly contribute to your projects
  • Members with sufficient rights can independently contribute to repositories
  • You update your local repositories relative to the GitLab repository with the git pull command
  • You send changes committed in your local repository to the GitLab repository with the git push command.

Content from Groups

Last updated on 2022-04-27 | Edit this page



  • How can I organize the projects of my (research) group and my collaborations?


  • Create a group.
  • Move a project.
  • Add a member to a group.
  • Explain how the members of a group relate to its subgroups and projects.
  • Delete a group.

In the first episode, we already got to know groups; GitLab’s way to organize projects. A group might hold a projects of a research group or those of a research project with contributors from multiple institutions. Together with members and their permissions they can be used to manage access to multiple repositories (for example, restrict access to certain repositories to staff while others may be accessed by student assistants as well).

In this episode we will create (and delete) a group, move our project, and learn how members of a group relate to its subgroups and projects.

Creating a Group

In the top menu bar, we open the drop-down menu labeled with a boxed plus and select the menu item “New Group”. On the next page, we select “Create group”, because that is what we want to do.

This leads to the following page:

Create group form with text input fields labeled “Group name”, “Group URL”, a radio button element labeled “Visibility Level” with options “Private” and “Public”, a drop-down field labeled “Role”, a radio button element labeled “Who will be using this group?” with options “My company or team” and “Just me”, a drop-down field labeled “What will you use this group for?” and an text input field labeled “Email 1” with a button to add more under the heading “Invite members (optional)”.
Create group form

Choose any name for the group, for example a fictional research group like “Ant-Sized Elephants’ Applications Lab”. When you move the focus out of the input field, for example by pressing the tabulator key, GitLab will suggest a URL for the group. Usually, we might want to adapt it, but for now all we care about that it is available.

You can leave the group’s visibility private. The visibility of a group works like that of a project.

We will leave the remaining fields as they are and create the group by clicking the blue button labeled “Create group”. GitLab will navigate to the group home page, listing the group’s subgroups and projects (currently none).

Now, we will have a look at how to create a project in the new group.

In the top menu bar, we open the drop-down menu labeled with a boxed plus and select the menu item “New Project”. On the next page, we select “Create blank project”.

This leads to the create blank project form page. We do not really want to create a project. Instead we have a look at the drop-down field under the heading “Project URL”. It will either show your username or one of the groups you belong to.

If the field does not show the URL of the group that we just created, clicking on the field should open a drop-down list which includes it and allows us to select it.

Submitting the form with our group’s URL selected would create a project in that group.

Move Project to Group

We will now move our project to our group.

On the side menu, we select “Settings” then “General”. On that page, we expand the section labeled “Advanced” and scroll down to the subsection labeled “Transfer Project”.

Now, please read the subsections notes, with an emphasis on the list of things to be aware of.

A few comments on the last two items of the list:

The second to last tells us that we will need to update our local repositories to point to the new location. Because we are moving the project to a group from our own projects, the URL will change. So the URL we specified when adding the remote to our local repository will no longer be valid. We will test this, but will not change the URL, because we will move the project back in a bit.

The last item on the list informs us about potential visibility changes. For example, moving a public project to a private group, will cause the project to become private.

Having carefully read and thought about the warnings, we are ready to move our project. We select the group that we previously created from the drop-down field and click the button labeled “Transfer project”.

A dialog appears and we confirm, that we are sure, by typing in the name of our project and clicking the button labeled “Confirm”.

The project will be transferred to the group.

To test that the URL changed, we switch to the shell and navigate to the directory containing our repository; Not the repository of our co-learner!


git remote get-url origin

{: .language-bash}

should tell us that our project points to the old project URL. So, let us try to run

git pull

{: .language-bash}

Already up to date.

{: .output}

That result is unexpected. The reason for this is that GitLab will redirect the old URLs to the new ones, as long as the old URLs do not point to a new project.

Adding Members to Group

Adding Members

Add your instructor as a member of your group with the role of “Guest”. The process is the same as to add members to a project.

Hint: After adding a member, you might need to reload the members page to see the new member in the list.

This exercise should take about 5 minutes.

Steps to solve the challenge:

  1. Navigate to the group home page.
  2. Select “Group information”, then “Members” from the side bar menu.
  3. Click the “Invite members” button.
  4. Fill in the dialog to invite your instructor with the “Guest” role.
  5. Click the “Invite” button.

After adding a member to the group, we have look at the member list of our research diary project.

The list has three entries: ourselves, our co-learner, and our instructor. We have the “Owner” role and, as its creator, are a direct member of the project. Our co-learner has the “Developer” role that we assigned them and are also a direct member of the project, as we added them directly to the project.

The entry we are really interested in is that of the instructor. We added them as a “Guest” to the group. Now, they show up in the list of this project with the same role. The reason is, as the column “Source” tells us, that our project is in our group, and our instructor is a member of that group.

That means, projects and groups inherit the members of the group they belong to.

Move project

Move your project back to your username space.

This exercise should take about 5 minutes.

Delete Group

Now that our group is empty again, we will delete it, to be polite users of free resources. (If you created a group, that you actually intend to keep, do not delete it.)

Delete a Group

Delete your group. You find the deletion form at the same position in the group’s settings, as the project deletion form in a project’s settings.

This exercise should take about 5 minutes.

Steps to solve the challenge:

  1. Navigate to the group home page.
  2. Select “Settings”, then “General” from the side bar menu.
  3. Expand the “Advanced” section and scroll all the way down.
  4. Click “Remove group”.
  5. Fill in the dialog and click “Confirm”.


  • Groups and subgroups are used to organize projects.
  • Groups can have members.
  • Projects and groups inherit members from the group they belong to.

Content from Issues

Last updated on 2022-05-02 | Edit this page



  • How can I use GitLab to manage development or a whole project?


  • Create an issue.
  • Comment on an issue.
  • Close an issue.
  • Use boards to get an overview the state of issues.

Create Issue

Before our excursion to learn about groups, we contributed lab notes to our co-learner’s repository. Having done that, we think we should be listed as contributors in the documentation of that repository. Since, we want our co-learner to do the honors, we create an issue in their repository, to document our desire.

Issues are a feature of GitLab that allows us to organize tasks by project and by group (and its descendant groups and projects). By default anyone that has read access to a project can file issues. This usually makes sense, because users can report their problems and ask their questions that way, even when they are not supposed change anything themselves.

With our co-learner’s help, we navigate to their project. On the side bar menu on the left we click the entry labeled “Issues” and on the following page we click on the button labeled “New issue”.

This leads us to the following form:

New issue form with a text input field labeled “Title” with subscript “Add description templates to help your contributors communicate effectively!”, the second and third word being a link, a drop-down field labeled “Type” showing the word “Issue“, a multi-line text input field with buttons for text formatting labeled “Description”, a check box labeled “This issue is confidential and should only be visible to team members with at least Reporter access.”, drop-down fields labeled “Assignee“, “Milestone”, and “Label” showing “Unassigned“, “Milestone”, and “Labels”, respectively, a date input field labeled “Due date”, and two buttons labeled “Create Issue” and “Cancel”.
New issue form

For our issue we provide a short, descriptive title, for example “Recognize outside contributors” and a description:

Outside contributors should be mentioned in the project’s documentation. Their
names could be listed in one of the following locations:

* a section of ``
* a separate `` file

Markdown Code Spans and Blocks

Markdown provides syntax for formatting text in a monospaced font, this is usually used to typeset code, because code is usually written to be readable in such fonts.

For a monospaced snippet within a normal paragraph, enclose the snippet in a single backquote (also called backtick or grave accent), for example

The filename `` will be set in a monospaced font.

will be typeset as:

The filename will be set in a monospaced font.

For a whole block of monospaced text, for example some lines of code, start and end the block with a line consisting of three or more backquotes, where the number should match for start and end, for example

# This python code will also be set in a monospaced font, which preserves
# relative indentation between lines.
def some_function():

will be typeset as:

# This python code will also be set in a monospaced font, which preserves
# relative indentation between lines.
def some_function():

We assign the new issue to our co-learner by selecting their account in the drop-down field labeled “Assignee”.

Then we click the button labeled “Create issue”. This redirects us to the new issue’s page. Near top we see its title followed by its description. That we created the issue is mentioned above the title. A text field below the description can be used to create a comment and a side bar on left lists more fields that could be used to further specify properties of that issue.

Add a Label

One of those, the field labeled ”Labels”, we will get to know, now.

We want to know when our co-learner starts to work on the issue. To communicate that fact through GitLab’s issue system we can use its Label feature. Issues can be defined for groups and projects. In the case of groups they are then also available to all its descendant groups and projects.

So, if we want to assign a label to the issue, we first have to create it. We click on the “Edit” link next to the “Label” field in the side bar. This opens a small dialog titled ”Assign labels”. If we had already defined labels, they would be listed here.

Since have not, we click the link “Create project label” in the small dialog. A new dialog opens titled “Create project label”. We call it “WIP” for work in progress and select our preferred color. Colors are often used to group labels, for example one color for labels referring to the subproject or subsystem the issue relates to, another color for the current progress of the issue (like our WIP label), etc. Then we click the button labeled “Create”.

This brings us back to the “Assign labels” dialog, this time with our new label listed. We select our label by clicking it and close the dialog by clicking on the x in the upper right corner.

Comment on an Issue

We provided a generic description of the issue in its description, but we want to make sure that our co-learner does not forget to include us. We intend to write a comment regarding our inclusion.

To support our case we want to reference our contribution. For this, we can use another feature of GitLab’s markdown: When we mention a commit of the project’s repository by its identifier, GitLab will automatically convert it to a link to the page of the commit.

We navigate to the project’s commit history by clicking the submenu item “Commits” of the menu item “Repository” in the side menu. There we find our commit at the top with its short identifier shown on the right. Right next to that short identifier is a button with an icon that looks like a clipboard. As its tooltip tells us, clicking it will copy the commit SHA (identifier). So, we click on the button.

We navigate back to our issue by clicking the menu item “Issues” in the side menu and clicking on the title of our issue in the opened list.

Using the copied commit identifier, we formulate our commit similar to

Please mention me as a contributor due to commit

and click the button labeled “Comment”. We notice, that GitLab uses and shortened version of the commits identifier for the link text, which makes it slightly more readable.

Before we move on, please note the button labeled “Close issue” under the comment text field. An issue starts in the open state and can be closed by clicking this button. When we type something into the comment field the label of the button changes to “Comment & close issue”, so we can add a final comment before closing the issue.

This is a very important feature, allowing us to distinguish between issues that have yet to be dealt with and those that have already been dealt with. We do not, in general, delete issues, because they are often useful to check whether a certain issue was already discussed or has already come up in the past.

Issue Boards

Now, we will have a look at boards, a feature that uses labels to visually organize a project’s issues.

We navigate to our project’s issue board by clicking the submenu item “Boards” of the menu item “Issues” in the side menu.

The following page loads:

Issue board page with a menu bar on the left and content on the right. At the top of the content area is a drop-down field with “Development” selected followed by a button labeled “New board” a search field, a check box labeled “Show labels”, and two buttons the first labeled “Edit board”, the second “Create list”. Below these elements are two columns, each with a title bar, one titled “Open”, the other “Closed”. In the “Open” column there is a box representing an issue, showing its title “Recognize outside contributors”, a label “WIP”, the issue number “#1” and an avatar of a user.
Issue board page

GitLab creates on board, called “Development”, by default for each project. This is the board we see. It has two so called lists configured, labeled “Open” and “Closed”, that list all open and closed issues of the project, respectively. These two lists cannot be deleted.

We will now create another list for issues that are work in progress, that is those that are labeled “WIP”.

We click on the button labeled “Create List”. A new list appears to the right of the “Closed” list. We are prompted to select a label. We select our label “WIP” and click the button labeled “Add to board”.

The new list is created between the “Open” and “Closed” lists. Those two will always be on the left and right end, respectively. We can reorder the lists created by us however we want between these two.

Note, that our issue is initially show in the “Open” and our new list. When we reload the page, it disappears from the “Open” list. In general, open issues that belong to one of the lists due to their labels will not appear in the “Open” list. Closed issues, however, will always only appear in the “Closed” list.

Boards can not only be used to get an overview of a project’s issues. They can also be used to manipulate their labels. If we drag an issue from one list to another, the label of the originating list will be removed and that of the target list will be added (unless it is one of the “Open” and “Closed” lists). Moving an issue to the “Closed” list will close the issue, while moving an issue out of the “Closed” list will open it.

We note that we added the “WIP” label to our issue, however, that is not accurate. Our co-learner should add the label whenever they start to work on it. To remove the label we drag the issue to the “Open” list.

We click on the issue’s title to navigate to its page. Initially the comment text field was directly under the description. By now, there is a list that chronicles the changes to the issue. It starts with the assignment to our co-learner (still from creating the issue), next we see that the “WIP” label was added by us, then comes our comment, and finally it lists that we removed the ”WIP” label again.

This helps keep track of who did what with an issue


  • GitLab has a feature to manage issues of a project.
  • Issues consist of titles and descriptions.
  • Issues can be open or closed.
  • Labels can be assigned to issues.
  • Users can be assigned to issues.
  • Boards can give an overview of issues and their stats.
  • An issue’s state can be manipulated in the board view.

Content from Merge Requests

Last updated on 2022-05-09 | Edit this page



  • How can I contribute to a project on GitLab?


  • Fork a project.
  • Create and edit a file on GitLab.
  • Create/Accept a merge request.
  • Mention an issue in GitLab markdown.

In the chapter on collaboration we learned how to contribute changes to a project that we are a member of. We also learned, in the previous chapter, that we can contribute to other projects by filing issues to report problems or ask questions. This episode we will learn about a way to suggest concrete changes to a GitLab project that we do not have write access on.

The feature that allows us to do this is called Merge Request, the corresponding feature on GitHub is called Pull Request. As a target for our merge request, we will use our co-learners repository. We we have write access to it, but to learn something new, we will pretend that we don’t.

We will create a merge request that adds a file listing the contributors, which will solve the issue that we filed during the last episode.

Create a Fork

Since we are a member of our co-learner’s project we could create a merge request within the project. But again, to learn how to do it for project’s of which we are not a member of, we will make ourselves some extra work.

So supposedly, we cannot make changes to our co-learner’s project. We need a copy that we can change. Such a copy is called a fork in GitLab’s (and also GitHub’s case). The term can be explained by looking at its use in “fork in the road”. Once we make changes to our copy, the development history will have split.

To create a fork, we navigate to the homepage of our co-learner’s project. In the upper right corner is a button labeled ”Fork”. The number next to it tells us how many forks of this repository exist on GitLab. We will now increase that number by one by clicking on the button.

This redirects us to a form that looks like a reduced version of the “Create new project” form. Since we do not want to make a real fork of the project, that is project that develops in a different direction than the original, but rather suggest a change to the original, we will leave almost everything as it is.

What we need to do is select a namespace. We will use our user namespace, so we select ourselves in the drop-down list under ”Project URL”. We set the visibility to private, because we will make the changes public (to those able to see the original project) once we create the merge request. Then we click the “Fork project” button.

Unique Project Name

If there already is a project with the name or the same slug, GitLab will show you an error message which, at the time of me writing this, can be summarized to “an error has occurred”, which is not very helpful. In that case, reload the page, select a different name and/or slug and try again.

We are redirected to the fork’s project home page. The fork is full featured project just like the original, however, only the Git repository is copied. So, for example, there are no issues in it, even though the original had at least one.

Create and Edit a File on GitLab

Our goal is to create a file listing contributors to the project. We will do this within GitLab using its file creation and editing features.

To do that, we click on the button labeled “Web IDE” in the line just below the project’s description. This redirects us to a page with a file browser in a column on the, with most of the screen taken up by a box informing us how to use the Web IDE: “Select a file from the left sidebar to begin editing. Afterwards, you’ll be able to commit your changes.”

We do not want to edit an existing file, but want to create a new one. To do that we click on the button above the list of files on the left that is marked with an icon representing a sheet of paper with a +-sign on top of it.

A pop-up dialog prompts us for a name, which we will provide: “” followed by clicking the button labeled “Create file”.

All caps filenames

In software projects it is customary to name certain files with information about a project in all caps. One example we already encountered is the README file. Others are the file containing the license (LICENSE), a file about how to contribute (CONTRIBUTING), or how to install the software (INSTALL).

Sometimes a file extension indicating the format is appended, usually in lower-case letters.

We see the result of our action in two ways: A file named “” appeared in the list of files on the left and a tab labeled “” showing an text editor appeared in the space to the right of the list of files.

We will now add something similar the following lines in the editor:


The following persons have contributed to my research diary:

- Conner Learner

Then we click the button labeled “Create a commit”. The view to the left of the list of files gets split into to columns. On the left side we see the old version of the file (it did not exist, so nothing is shown) and on the right side we see the new version with additions marked in green. If we had removed anything from the old version those lines would have been marked red on the left side.

Since everything looks as expected—one new file with called “” with the lines we just typed in—we change the commit message to


We keep “Create a new branch” with the pre-generated name selected. For our purposes we could also leave the checkbox “Start a new merge request” selected, but we want to go through the merge request creation process separately to get familiar with it, in case we want to create one from changes we pushed from a local repository.

We do create a new branch and do not commit the changes to the branch called “main”, because it is a good habit. We might want to create multiple independent merge requests for one project, which cannot be done from a single branch. And then there is the option to create merge requests within a project, those cannot come from the branch they are supposed to be merged to, so we need another branch anyway.

Finally, we click the button labeled “Commit”.

Create a Merge Request

Having created the commit, we navigate back to the project home page by clicking on the project’s name in the upper left corner.

We notice that the file that we created is not present in the list of files. This is because GitLab shows the default branch, in our case the branch called “main”, by default. When we click the drop-down field below the description and the sentence starting with “Forked from…”, we get shown a list of all branches, which should be two: “main” and the one generated for our change, something along the lines of “…-patch-12345”. We select the new branch and now the file “” shows up in the list of files.

To create a merge request, we click on the menu item labeled “Merge requests” in the menu on the left and on the following page on the button labeled “New merge request”.

This gets us to the following dialog:

New merge request page. On the left, a menu leading to other project pages. ON the right, taking up the major part of the width, the new merge request page: Below the page title, there are two UI elements side-by-side. The one on the left, titled ”Source branch”, has two drop-down fields, one to select the project, the other to select the branch. The former reads “fiveop/research-diary-2”, the latter “Select source branch”. The one on the right, titled “Target branch”, has the same two drop-down fields. In this case the former reads “somenovice/research-diary” and the latter ”main”. Belowthe two drop-down fields there is a box with information about the last commit on the “main” branch: message, author, relative author date and short identifier.
New merge request page

On the left side, we select the branch generated for our change. Once done, a box with details of the last commit on that branch appears below the drop-down fields on the left.

We click the button labeled “Compare branches and continue”. On the following page, we can see that a merge request has it’s own title and description. They are prefilled with the newest commit message of the source branch. To connect this merge request with the issue we filed, we write

Resolves #1

in the merge request description. Everything else, we leave with its default value or in its default state, including the two checkboxes.

Closing Issues Automatically

Whenever a merge request is merged to the default branch of a project and its description or the description of a commit it contains the phrase <keyword> #<number> for one of the keywords mentioned in GitLab’s documentation, then the issue with number <number> is closed automatically.

This can save a few clicks and ensures that closing the issue will not be forgotten.

The first, labeled ”Delete source branch when merge request is accepted”, is selected and thus let’s GitLab clean up after us; we no longer need the branch once it has been merged.

The second, labeled “Squash commits when merge request is accepted”, is not selected. If it were, GitLab would merge all commits of the merge request, that is all commits on the source branch since it was branched off the target branch, into one commit before merging. In our case, there is only one commit, so it would serve no purpose.

We click the button labeled “Create merge request”. This leads us to the page for the created merge request. It looks similar to the page for an issue. The main difference are the tabs, their headers being located just below the title: “Overview” (the one that is shown initially), “Commits”, and “Changes”.

Clicking on the tab header labeled “Commits”, we see a list of all the commits of this merge request, which in our case is the commit we created.

Clicking on the tab header labeled “Changes”, we see a so called diff for each of the changed files. In our case a file was added, so all the lines in the file are new, indicated by the plus sign at the beginning of each line.

Back on the “Overview” tab, below the description there is a technical description of the merge request, starting with “Request to merge…”, followed by a button to “Approve“ the merge request. By default this is optional, a project can be configured to require approval by a certain group of members for a merge request to be possible. Then there is a grayed-out button labeled “Merge”, followed by text that we need to ask someone with write access to click it. We will do exactly that in a moment. Note also, that the box around it mentions that the merge request mentions the issue. This can be easily overlooked, if the mention happens in a commit message of the merge request.

Finally at the bottom, we see that a merge request can be commented on, just like an issue. But for merge request there is more. Navigating to the “Changes” tab and hovering the mouse over a line number, we see a speech bubble button appear at the start of the line. When we press it, a text field for a comment appears. We can use that to comment on individual lines. This feature allows to separate discussions of different changes of a merge request.

Code Review

Being able to comment on individual lines is a useful feature for a practice called code review. Code review entails that a peer of the author of some changes reviews those, before they are merged. This practice is the origin of the merge request feature, but it lends itself well to reviewing text based changes other than to code.

Accept a Merge Request

While we were creating the merge request for our co-learner’s repository, they did the same for ours. Now it is time to review and accept the change.

Navigate to the issue your co-learner created in your repository. At the very bottom of the issue’s history you will see en entry of the form “Co-learner @colearner mentioned in merge request !1 2 minutes ago”. The merge request reference, ”!1”, is a link. We follow the link by clicking on it.

This leads us to the merge request page, very similar to the page of our own merge request. The difference, that we are interested in, is the state of the button labeled “Merge”. The button is no longer disabled. Under the button is a bar that informs us that one commit and one merge commit will be added.

The merge commit serves as a common descendant of the heads of your main branch and the branch on which your co-learner based their merge request. We can change the commit message of the merge commit, by clicking on the bar informing us about the number of added commits, but we are happy with its contents.

We click on the button labeled “Merge”. After a few seconds the box that allowed us to merge is replaced by a box that informs us about the successful merge.

In particular, there still is a sentence informing us that the merge request mentions an issue. Our intention was for that issue to be closed, so we click on the issue reference, for example “#1”, to check.

On the issue page, we can see that indeed the issue was closed. The last entry in the issue’s history reads similar to “Co-learner closed via commit abcdef12 just now”, informing us why the issue was closed.


  • You can create a project with a copy of another project’s repository by forking it.
  • You can suggest changes to a project that you have no write access to through a merge request.
  • You can close issues by use of certain keywords in merge request descriptions or commit messages.

Content from Process Automation

Last updated on 2022-05-16 | Edit this page



  • How can I use GitLab to automate processes?


  • Setup a process to run automatically when a commit is pushed to a repository.
  • Safely use secrets in a process automatically triggered by GitLab.

We have setup our project, collaborated with our colleagues; we fill our research diary every day. Now it is time to make use of it.

We think about using part of our daily commute by train to review what happened in the past. We always have our e-reader with us, so ideally we would like to have an version of our diary in EPUB format.

Just as much based in reality as the rest of this lesson’s story, we know how to do that, but we do not want to do it by hand. Instead, we are going to let GitLab automatically build us an EPUB file, whenever our repository changes. To achieve it, we will us the feature that GitLab files under “CI/CD”, which stands for Continuous Integration/Continuous Deployment.

Continuous Integration/Continuous Deployment

The terms continuous integration (CI) and continuous deployment (CD) come from the fields of software engineering and software operation.

A software project uses CI, if it has setup a process that automatically runs some (or all) tests for a software project, whenever changes are committed or even just proposed (for example through a merge request) to the main branch of a repository. In particular when already done for proposed changes this can help prevent bugs to reach the code base, while freeing the developer of thinking of and running all the tests themselves.

CD is a process that updates the software on the production machines (deploys) as soon as a new change reaches the code base, generally after having passed the CI process.

Both processes can be implemented by running scripts, called jobs, triggered either by changes to the code base or by the successful completion of other jobs, which lead to the CI/CD feature of GitLab.

Example Configuration

To configure our automatic process, we navigate to our project page and click the menu item labeled “CI/CD” in the main menu on the left.

The page that this leads to invites us to “use a sample .gitlab-ci.yml template file to explore how CI/CD works.” The GitLab CI is configured by providing a file called .gitlab-ci.yml in the root directory of a project’s repository. Even though we want to learn how CI works in GitLab, we do not follow the invitation, because the example is quite elaborate and targets software developers specifically. Instead, we will use the provided template for Bash.

We click on the button labeled “Use template” of the entry for Bash in the list at the bottom of the page.

This leads us to an editor for the .gitlab-ci.yml file that is prefilled from the selected template. The file is expected to be written in the YAML file format, hence the file extension .yml. We will go through the example line by line. Afterwards we will adapt it to our needs.

The first few lines, starting with the symbol #, are comments notifying us of where to find further information. The last of these lines is the exception, which is a comment on the first functional line:


image: busybox:latest

This line states that the scripts that are provided later on should be executed in Docker containers build from the stated Docker image. Busybox is a very small Linux distribution, reduced to the bare minimum, and it provides a shell.



Now follow to blocks starting with before_script: and after_script:. We will ignore those.

The remaining four blocks, build1:, test1:, test2:, and deploy1:, each define a so called job. A job defines a script and represents one non-divisible unit of a CI/CD process. Together the jobs defined in a project’s .gitlab-ci.yml file form a so called pipeline.

By default a pipeline has three stages, called build, test, and deploy, and their order is important. The terminology comes from software development again. A pipeline is executed by running all jobs of the first stage and if they succeed to continue onto the next stage, continuing until a job fails or all jobs of the last stage succeeded.

The four jobs in the example are named similar to the stages they are assigned to. The assignment is done through the keyword stage: that can always be found in the second line of a job’s definition.

The lines following the keyword script: in each section, define the script that will be executed as part of the respective job. In this example, they are all echo statements.

Our Own Configuration

We will now adapt the script to our requirements.

First we delete everything.

Then we define our own stages, because we do not develop software and want to have descriptive names for our use case:


    - check
    - create

In the first stage, we will do some testing, in the second we will create an EPUB format version of our research diary.

We call the first job check-for-mds and in it make sure, that at least one Markdown file exists, because otherwise someone must have accidentally removed them all.


    image: bash:latest
    stage: check
        - compgen -G '*.md'

We use the docker image bash, because Bash provides the compgen command that allows us to check for the existence of files. We also state that the job belongs to the stage check. Finally, we provide the script. It tests for the existence of any files with names ending in .md.

Whenever the job check-for-mds successfully completes, we want to create the EPUB format version of our research diary. We define a second job and call it create-epub.


        name: pandoc/core:latest
        entrypoint: ["/bin/sh", "-c"]
    stage: create
        - pandoc *.md -o diary.epub
            - diary.epub

After giving the name of the job, we again provide an Docker image in which the job should run. Since we want to use pandoc to convert our Markdown files into an EPUB file, we use the official pandoc Docker image. The next line, with the keyword entrypoint is necessary, because the pandoc Docker image is configured to directly run pandoc, when started in a container (pandoc is configured as its entrypoint). However, GitLab CI expects to run scripts in the Docker container, thus expects the Docker images to start a shell.


Pandoc converts text documents from format to another, for example from Markdown to EPUB. It supports many formats for the source documents and even more for the target document.

The project webpage provides a complete list (in text and graphical form).

Next, we specify the stage, followed by the script. It runs pandoc on all files ending in .md in the current directory and instructs it to output a file diary.epub. Pandoc deduces from the file extension that it should be in EPUB format.

The final three lines, starting with the keyword artifacts, specify that GitLab CI should save the file diary.epub from the Docker container the job runs in and provide it for download on its web interface.

This completes our GitLab CI configuration.There are a lot more configuration options, for example to have a process run only for commits to certain branches, that are documented in GitLab’s Handbook

We change the commit message to ”Configure CI“, leave the branch as it is (main), and click the button labeled “Commit changes”.

Shared Runners

The software that runs CI jobs is called a runner. GitLab provides so called shared runners on its platform for free (within certain usage limits). It does, however, require the user to provide credit card information. This is intended to prevent abuse of the free resource.

If no such information has been provided running the CI will always fail.

Alternatively, the organizers of a course based on this material could setup their own runner and help learners to configure their projects to use that runner.

On other instances the situation can be different.

Clicking the button does not cause a change in page. Instead, we are informed at the top of the page about the status of the pipeline run that was initiated by committing the .gitlab-ci.yml to the repository.

To get a better overview, we select “CI/CD” from the main menu on the right side. This brings us to the list of pipeline runs. We should see exactly one entry in the list, as we have just configured the CI.

A blue, red, or green box in the “Status” column informs on the left informs us about the state of the pipeline. Blue indicates a running pipeline, red a failed pipeline, and green a pipeline that successfully completed. Below the status box we have the runtime of the pipeline and below that information about when it was started.

In the ”Pipeline” column, we see the first line of the commit that triggered the pipeline run, the number of the pipeline run, for example #11071, the branch the commit is part of as well as the short identifier of the commit. Most of these serve as links to pages for their respective entities.

The ”Triggerer” column tells us who caused the pipeline run. In our case that’s the author of the commit.

The ”Stages” column visualizes the stages and their state. In addition to the colors discussed above, there are stages colored in grey. They are waiting for their predecessor stages (or need to be manually triggered, if configured that way).

We wait until our pipeline ran through. It should complete without error.

We click on the button labeled by the three dots on the right of the entry. In the menu that opens, we see that it provides a link to download artifacts of the pipeline. We click on the link labeled “create-epub:archive”, which causes our browser to download a file called

In that archive, we find the file diary.epub that was build by our second job, create-epub.

If you have an ebook viewer, you can verify that it contains the contents of all Markdown our files. (We ignore the fact, that the order might not make sense at all. For that we would need to improve the script that builds the EPUB file.)