GitLab Basics

Page 04 — What GitLab adds on top of Git, and how to work with it day to day.

What GitLab is

GitLab is a platform built around Git repositories. Git stores the history. GitLab manages the repo on a server and adds workflow on top of it:

Add your SSH key to GitLab

Show your public key:

cat ~/.ssh/id_ed25519.pub

Paste it into GitLab → Preferences → SSH Keys. Then test the connection:

ssh -T git@your-gitlab-host
Tip: If the test returns a welcome message with your username, SSH auth is working. If it says "Permission denied", the key is not set up correctly.

Normal workflow

git checkout main
git pull origin main
git checkout -b feature/change
# edit files
git add .
git commit -m "Describe change"
git push -u origin feature/change

Then in GitLab:

  1. Open a Merge Request from your branch to main
  2. Review the diff — make sure it only contains what you intended
  3. Check that the pipeline passes
  4. Merge when approved

Merge Request

A Merge Request (MR) is GitLab's review flow for combining your branch into another branch, usually main. You use it so:

Useful MR terms:

Pipeline

A pipeline is a set of automated jobs that run on every push, defined in .gitlab-ci.yml. Common jobs include linting, syntax checks, tests, and build steps.

If a pipeline fails, GitLab is telling you something needs fixing before the MR can merge. Check the job output for the specific error.

Common pipeline failures:

Common problems

Auth failed

Push rejected

Check and change remotes

git remote -v

Shows which remote URLs your repo uses. Check this when auth issues are unclear.

Switch from HTTPS to SSH:

git remote set-url origin git@host:group/repo.git

Personal Access Token (HTTPS auth)

If you can't use SSH keys (e.g. on a shared machine, or when cloning in a script), use a Personal Access Token (PAT) for HTTPS authentication.

Create a PAT in GitLab

  1. Go to your GitLab profile: User menu → Edit profile → Access Tokens
  2. Give the token a name (e.g. laptop-cli)
  3. Set an expiry date
  4. Select the minimum required scopes:
    • read_repository — clone and pull only
    • write_repository — push and create branches
    • api — full API access (only needed for scripts/automation)
  5. Click Create personal access token and copy the value — you only see it once

Using a PAT to clone

# Embed token directly in the URL (not recommended for interactive use — stored in shell history)
git clone https://your-token@gitlab.internal/group/repo.git

# Better: use the username + token prompt
git clone https://gitlab.internal/group/repo.git
# Git will prompt for username (enter your GitLab username)
# and password (enter your PAT — not your account password)

Credential helper — avoid re-entering the PAT

# Store credentials in plaintext (simplest — acceptable on private machines)
git config --global credential.helper store
# After your next push/pull with credentials, Git stores them in ~/.git-credentials

# On macOS — use the system keychain
git config --global credential.helper osxkeychain

# On Linux — use libsecret (GNOME keyring)
git config --global credential.helper /usr/lib/git-core/git-credential-libsecret

The store helper saves credentials unencrypted to ~/.git-credentials. On a shared or multi-user machine, use a keychain-based helper instead.

Switching from HTTPS to SSH: Once you have SSH keys set up, switch existing repos with git remote set-url origin git@gitlab.internal:group/repo.git.

Create MR from the terminal

You can create a GitLab Merge Request directly from the git push command — no browser required. GitLab prints the MR URL in the push output.

# Push and create an MR in one command
git push -o merge_request.create origin feature/add-chrony-role

# Push, create MR, and set the target branch (default is usually main)
git push \
  -o merge_request.create \
  -o merge_request.target=main \
  origin feature/add-chrony-role

# Create as a Draft MR (blocks accidental merge until ready)
git push \
  -o merge_request.create \
  -o merge_request.target=main \
  -o merge_request.title="Draft: Add chrony NTP role" \
  origin feature/add-chrony-role

# Assign to a reviewer by username
git push \
  -o merge_request.create \
  -o merge_request.assign=johndoe \
  origin feature/add-chrony-role

GitLab will print the MR URL after a successful push. You can share this URL in Slack, a ticket, or a PR comment immediately.

Default roles

GitLab's five built-in roles are layered — each one includes everything the role below it can do. Roles are assigned per project or per group (group roles are inherited by every project in the group).

RoleCan doTypical use
Guest View public issues, see wiki/snippets, create issues. No code access on private projects. External stakeholders, read-only observers.
Reporter Everything Guest can, plus: pull/clone code, view pipelines, manage issues, view MRs. PMs, support, anyone who reviews work without pushing.
Developer Reporter + push to non-protected branches, create MRs, run/cancel jobs, manage labels and milestones. Cannot push to protected branches by default. The standard role for engineers contributing to a project.
Maintainer Developer + push to protected branches, merge MRs, manage CI/CD variables, manage runners, edit project settings, add members (up to Maintainer). Tech leads, code owners, release managers.
Owner Full control: delete project/group, change visibility, transfer ownership, manage all members and billing. Group admins only. Keep this list short.

Protected branches are the main reason Developer vs Maintainer matters in practice. For day-to-day review workflow you want most engineers at Developer and a small team of code-owners/leads at Maintainer.

Tokens — PAT, deploy, project access, CI_JOB_TOKEN

GitLab exposes four distinct token types. Picking the wrong one is the single most common cause of "why can't my job clone that other repo?".

Personal Access Token (PAT)

Tied to your user account. Has the same permissions as you on every project and group you can see. Covered above in Personal Access Token.

Best for: interactive CLI use, local scripts tied to your identity. Worst for: shared automation — if you leave the company the tokens stop working.

Deploy tokens

# Create in: Project → Settings → Repository → Deploy tokens
# Scopes: read_repository, read_registry, write_registry, read_package_registry, write_package_registry
git clone https://gitlab+deploy-token-42:TOKEN@gitlab.example.com/group/repo.git

Scoped to a single project, not a user. Typically read_repository only — used by external systems (e.g. a Packer build VM) that need to clone but shouldn't have write access.

Project access tokens

A bot user scoped to a single project (or group access token for a whole group). You pick a role (Guest→Maintainer) and scopes (api, read_repository, write_repository…). The token is a user account called something like project_42_bot, so it counts toward license seats on paid tiers but survives people leaving.

Best for: long-lived automation that belongs to a project (deploy pipelines, renovate bots, ChatOps bots).

CI_JOB_TOKEN

# Inside a .gitlab-ci.yml job — no setup needed
script:
  - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.example.com/group/other-repo.git
  - curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/123/packages/..."

Automatically injected into every CI job. Lives only for the job's duration. By default it can clone the current project; to let one project's jobs clone another, add the source project under Target → Settings → CI/CD → Job token permissions → Allowed to authenticate. Prefer CI_JOB_TOKEN over a stored PAT whenever it is enough — there's nothing to rotate and nothing to leak into logs.