GitLab 04 🦊 Working with Repositories, Branches, and Protected Branches
A repository in GitLab is more than a Git directory. It is a project with a default branch, a set of permissions inherited from the namespace hierarchy, and a set of protected branches that enforce who can push and who can merge. The Git commands are the same as anywhere else—git clone, git checkout -b, git push—but the constraints applied to the branch are configured in GitLab, not in Git. The exam tests the conceptual model: the default branch is the base for merge requests, protected branches restrict push access, and the main branch is protected by default for Maintainers only.
Key point: A protected branch is not a Git feature. It is a GitLab-enforced rule that blocks direct pushes from users with a role lower than the configured “Allowed to push” role. The default configuration for main allows Maintainers to push and Developers to merge, but Developers cannot push directly to main.
Why repositories and protected branches exist
The direct push problem. In a Git repository without protections, any user with write access can push a commit directly to main. This bypasses code review, bypasses CI checks that run on merge requests, and can break the main branch. Protected branches block this by restricting push access to a specific role—typically Maintainers—and requiring merge requests for everyone else .
The force push problem. git push --force rewrites the branch history. If main is not protected, a force push can erase commits that other users depend on. Protected branches block force pushes by default. The “Allowed to force push” setting is disabled unless explicitly enabled, and even then, it is restricted to the configured role .
The default branch problem. Every repository has a default branch. The default branch is the base for merge requests, the branch that is checked out when the repository is cloned, and the branch that CI/CD pipelines run against when no branch is specified. GitLab sets the default branch to main for new repositories, but it can be changed in the project settings .
The merge request problem. A merge request proposes merging a source branch into a target branch. If the target branch is protected, the merge requires an approval from a user with the merge role. The number of required approvals is configured in the merge request approvals settings, not in the branch protection rule. The two settings are separate: the protected branch controls who can push and merge; the approval rule controls how many approvals are required.
The trade-off. Protected branches slow down the workflow. A developer who wants to fix a typo must create a branch, commit, push, open a merge request, wait for approval, and merge. The alternative—pushing directly to main—is faster but bypasses review, CI, and the safeguards that prevent production breakage. The protection is the cost of reliability.
a. Creating a branch and pushing it
A branch is created with git checkout -b (or git switch -c) in the local repository. The branch is pushed to GitLab with git push. The branch is not protected unless a rule matches its name.
# Create a branch
git checkout -b feature/login
# Make changes and commit
git add .
git commit -m "Add login form"
# Push the branch to GitLab
git push -u origin feature/login
The -u flag sets the upstream branch, so subsequent git push and git pull commands do not require the branch name. The branch appears in the repository’s branch list and in the merge request creation page.
Branches can also be created directly in GitLab. The “New branch” button on the Repository → Branches page creates a branch from the current default branch or from another branch. This is useful for hotfixes or for creating a branch to receive a merge request.
b. Configuring protected branches
Protected branches are configured in the project settings: Settings → Repository → Protected branches. The configuration is a list of branch name patterns, each with a set of permissions.
Protected branches list:
main | Allowed to merge: Maintainers | Allowed to push: Maintainers
release/* | Allowed to merge: Maintainers | Allowed to push: No one
feature/* | Allowed to merge: Developers | Allowed to push: Developers
The main branch is protected by default on new repositories. The default configuration allows Maintainers to merge and push, and blocks Developers from pushing. The release/* pattern uses a wildcard, so any branch starting with release/ is protected. The “Allowed to push” is set to “No one”, which means only merge requests can update the branch.
The “Allowed to force push” setting is a separate toggle. When enabled, users with the push role can use git push --force. When disabled (the default), force pushes are blocked. The “Code owner approval” setting requires approval from the code owner if the branch is modified.
The “Require approval from code owners” setting is distinct from the code owner approval in merge request approvals. The branch protection setting enforces code owner approval for every merge into the protected branch, regardless of the merge request approval rules.
c. The default branch and merge request workflow
The default branch is set in Settings → Repository → Default branch. Changing the default branch affects new clones, new merge requests, and the default target for pushes.
Settings → Repository → Default branch
Change to "develop"
Save changes
The default branch is not protected by default unless it matches a protected branch pattern. In a repository where the default branch is develop, a new main branch may exist but not be the default. The protection rules are matched by name, not by default status.
A merge request is opened from a source branch to a target branch. The target branch is typically the default branch (main). The source branch is the feature branch. The merge request page shows the diff, the commits, the pipeline status, and the approval status.
# Create a merge request
Navigate to Repository → Branches
Click "New merge request" next to the feature branch
Target branch: main
Fill in title and description
Create merge request
The merge request can be merged only when the required approvals are satisfied and the pipeline has passed. If the target branch is protected, the merge is performed by a user with the merge role, not by the merge request author.
Complete Example Session
# ============================================
# PART 1: CLONE THE REPOSITORY
# ============================================
git clone git@gitlab.com:group/project.git
cd project
# ============================================
# PART 2: CREATE A FEATURE BRANCH
# ============================================
git checkout -b feature/login
# Make changes...
git add .
git commit -m "Add login form"
# ============================================
# PART 3: PUSH THE BRANCH
# ============================================
git push -u origin feature/login
# The branch is created on GitLab.
# ============================================
# PART 4: OPEN A MERGE REQUEST
# ============================================
# GitLab → Repository → Branches → New merge request
# Source: feature/login
# Target: main
# Create merge request
# ============================================
# PART 5: CONFIGURE PROTECTED BRANCH
# ============================================
# Settings → Repository → Protected branches
# Branch: main
# Allowed to merge: Maintainers
# Allowed to push: Maintainers
# Allowed to force push: disabled
# Save
# ============================================
# PART 6: OBSERVE PUSH DENIAL FOR DEVELOPER
# ============================================
git checkout main
git pull
git commit --allow-empty -m "Test direct push"
git push origin main
# Error: You are not allowed to push code to protected branches.
# ============================================
# PART 7: MERGE THE MERGE REQUEST
# ============================================
# A Maintainer opens the merge request.
# Pipeline passes, approvals satisfied.
# Merge button is enabled.
# Click "Merge".
# ============================================
# PART 8: DELETE THE SOURCE BRANCH
# ============================================
git push origin --delete feature/login
# The branch is deleted after merging.
# ============================================
# PART 9: CHANGE THE DEFAULT BRANCH
# ============================================
# Settings → Repository → Default branch
# Change to "develop"
# New clones will check out "develop".
The nine parts trace the workflow: clone, branch creation, push, merge request, protected branch configuration, push denial, merge, branch deletion, and default branch change.
Quick Reference
Branch Commands
| Command | Purpose |
|---|---|
git checkout -b <branch> | Create and switch to branch |
git push -u origin <branch> | Push and set upstream |
git branch -a | List all branches |
git push origin --delete <branch> | Delete remote branch |
Protected Branch Settings
| Setting | Options |
|---|---|
| Allowed to merge | No one, Maintainers, Developers |
| Allowed to push | No one, Maintainers, Developers |
| Allowed to force push | Enabled/Disabled |
| Require code owner approval | Enabled/Disabled |
Default Branch
| Action | Location |
|---|---|
| Change default branch | Settings → Repository → Default branch |
Best Practices
✅ Do This:
# Protect main with Maintainers only
<!-- Developers use merge requests --> # ✅
# Use wildcards for release branches
<!-- release/* pattern protects all releases --> # ✅
# Disable force push on protected branches
<!-- Prevents history rewriting --> # ✅
❌ Don’t Do This:
# Push directly to main
<!-- Fails on protected branch; use MR --> # ❌
# Force push to a protected branch
<!-- Blocked by default; use with caution --> # ❌
# Assume the default branch is protected
<!-- Protection is matched by name pattern --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Cannot push to main | Protected branch | Create a merge request |
| Force push rejected | Protection enabled | Disable in protected branch settings |
| Default branch not protected | Name does not match pattern | Add pattern to protected branches |
| Merge request cannot merge | Approvals not satisfied | Add required approvals |
| Branch not visible | Not pushed | git push -u origin <branch> |
Real-World Examples
1. Create Branch
git checkout -b feature/login
2. Push Branch
git push -u origin feature/login
3. Protect Main
Settings → Repository → Protected branches
Branch: main
Allowed to push: Maintainers
4. Wildcard Protection
release/* → Allowed to push: No one
5. Open Merge Request
Repository → Branches → New merge request
6. Merge Request Approval
Merge request → Approvals → Approve
7. Delete Branch
git push origin --delete feature/login
8. Change Default Branch
Settings → Repository → Default branch → develop
9. Force Push Toggle
Protected branch → Allowed to force push: Enabled
10. Code Owner Approval
Protected branch → Require code owner approval: Enabled
Visual
Branch Protection Flow
┌──────────────────────────────────────────────┐
│ Developer pushes to feature/login │
└──────────────────┬───────────────────────────┘
│
│ allowed (not protected)
▼
┌──────────────────────────────────────────────┐
│ Branch exists on GitLab │
└──────────────────┬───────────────────────────┘
│
│ merge request to main
▼
┌──────────────────────────────────────────────┐
│ main is protected │
│ ├── Developer cannot push │
│ ├── Merge requires Maintainer │
│ └── Approvals required │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Maintainer approves and merges │
└──────────────────────────────────────────────┘
Default vs Protected Branch
┌──────────────────────────────────────────────┐
│ Default branch │
│ ├── Checked out on clone │
│ ├── Target of merge requests │
│ └── NOT automatically protected │
└──────────────────┬───────────────────────────┘
│
│ explicit configuration
▼
┌──────────────────────────────────────────────┐
│ Protected branch │
│ ├── Push restricted by role │
│ ├── Force push disabled (default) │
│ └── Merge requires role and approvals │
└──────────────────────────────────────────────┘
The two concepts are separate. A branch can be the default and unprotected, or protected and not the default.
Summary
| Item | Value |
|---|---|
| Default branch | main for new repositories |
| Default protection | main is protected by default |
| Allowed to push (default) | Maintainers |
| Allowed to merge (default) | Maintainers |
| Force push (default) | Disabled |
| Wildcard patterns | release/*, feature/* |
| Default branch location | Settings → Repository → Default branch |
| Protected branches location | Settings → Repository → Protected branches |
Key takeaways:
- Protected branches are GitLab-enforced, not Git features. The rule restricts push and merge access by role. A Developer cannot push to
mainif the protection rule allows only Maintainers . - The default branch is not automatically protected. A branch can be the default without matching a protected branch pattern. The protection rules are matched by name, not by default status .
- Force push is disabled by default on protected branches. The “Allowed to force push” toggle must be explicitly enabled to permit history rewriting. It should be enabled only when required and only for trusted roles .
- Merge requests are the merge mechanism for protected branches. The developer creates the branch, pushes it, and opens a merge request. The Maintainer approves and merges. The direct push is blocked by the protection rule .
- Wildcards protect families of branches. A
release/*pattern protects every branch that starts withrelease/. This is the mechanism for protecting release branches without naming each one individually.
Remember: A branch is just a pointer in Git. The protection is the GitLab layer that enforces who can move the pointer. The default branch is the base for merge requests and clones. The protected branch is the gate for the merge. The two are configured separately, and the protection is matched by name pattern, not by default status.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!