The “Oh Crap” Moment
You push to main and get this:
git push origin main
remote: GitLab: You are not allowed to push code to protected branches on this project.
You never created a feature branch. All your commits went straight to main.
Your commits are safe, and this takes about 2 minutes to fix.
If you’re seeing this error, it means your team has branch protection enabled on the main branch. This is actually a good thing - it prevents anyone (including you) from accidentally pushing broken code directly to production. The protection is working as intended; you just forgot to follow the workflow.
Learn more about why we use branch protection in our Git branching strategy guide.
What Went Wrong?
Your Git history right now:
Local main is ahead of remote main by a few commits. The remote won’t accept the push because main is protected.
The plan:
- Move your commits to a new feature branch
- Reset local
mainto match remote - Push the feature branch
- Create a merge request
No commits will be lost. We’re just relocating them.
The Fix: Move Commits to Feature Branch
You should be on the main branch with commits that can’t be pushed.
Step 1: Check Your Current Status
git status
git log --oneline -5
This tells you:
- Which branch you’re on
- How many commits ahead you are
- What your recent commits look like
Example output:
On branch main
Your branch is ahead of 'origin/main' by 3 commits.
a1b2c3d (HEAD -> main) Fix: Update user validation logic
e4f5g6h Add: User authentication endpoint
i7j8k9l Refactor: Database connection handling
Those 3 commits need to move.
Step 2: Create Feature Branch From Current Position
This branches right where you are, preserving all commits:
git checkout -b feature/user-authentication
Replace feature/user-authentication with something appropriate. Follow your team’s branch naming convention.
Your new branch now has all your commits, and you’re on it.
Step 3: Verify Feature Branch Has Your Commits
git log --oneline -5
All your commits should be there. They’re safe on the feature branch.
Step 4: Switch Back to Main and Reset It
Reset main to match remote:
# Switch back to main
git checkout main
# Reset main to match remote (THIS IS SAFE - your commits are on feature branch)
git reset --hard origin/main
This makes local main identical to origin/main, discarding commits that now live on your feature branch.
Never run git reset --hard on a branch with work you haven’t saved elsewhere. We’re only doing this on main
because we already copied the commits to the feature branch in Step 2.
Step 5: Verify Main Is Reset
git status
git log --oneline -5
Expected output:
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
Main is clean again.
Step 6: Push Feature Branch and Create Merge Request
Switch back and push:
# Switch to feature branch
git checkout feature/user-authentication
# Push to remote (creates new branch on GitLab/GitHub)
git push -u origin feature/user-authentication
The -u flag (--set-upstream) creates the remote branch and sets up tracking for future pushes.
Step 7: Create Merge/Pull Request
Create an MR/PR through your platform’s UI or via CLI:
GitLab:
# If you have GitLab CLI installed
glab mr create --title "feat: Add user authentication" --description "Adds JWT-based authentication for API endpoints"
GitHub:
# If you have GitHub CLI installed
gh pr create --title "feat: Add user authentication" --body "Adds JWT-based authentication for API endpoints"
Both platforms also show a direct URL in your terminal after pushing.
Visual: What We Just Did
- main branch: In sync with remote
- feature branch: Has all your commits, ready for MR
- Your work: Safe and in the right place
Verification Checklist
Quick sanity check:
-
git statusonmainshows “up to date with origin/main” -
git logonmaindoesn’t show your recent commits -
git checkout feature/user-authenticationswitches to feature branch -
git logon feature branch shows all your commits - Feature branch exists on remote (check GitLab/GitHub UI)
- Merge request is created and shows your changes
Preventing This (Because We All Forget)
1. Git Aliases for Common Workflows
Add these to your ~/.gitconfig:
[alias]
# Create feature branch and switch to it in one command
feature = "!f() { git checkout -b feature/$1; }; f"
# Before starting work, ensure you're on main and up to date
sync = "!git checkout main && git pull origin main"
# Start new feature (syncs main, creates feature branch)
start = "!f() { git checkout main && git pull origin main && git checkout -b feature/$1; }; f"
Usage:
# Old way (easy to forget checkout)
git checkout main
git pull
git checkout -b feature/my-feature
# New way (one command)
git start my-feature
2. Pre-Commit Hook to Warn on Main
Create .git/hooks/pre-commit in your repository:
#!/bin/bash
# Get current branch name
branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
# Check if on main/master
if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
echo "⚠️ WARNING: You're committing directly to $branch!"
echo "Are you sure you want to do this? (y/n)"
read -r response
if [ "$response" != "y" ]; then
echo "Commit cancelled. Create a feature branch first:"
echo " git checkout -b feature/my-feature"
exit 1
fi
fi
Make it executable:
chmod +x .git/hooks/pre-commit
This warns you every time you try to commit on main.
Conclusion
This happens to everyone. Branch protection catching it means the system is working.
The fix:
- Create feature branch from current position (saves commits)
- Reset local main to remote (cleans up main)
- Push feature branch (gets work where it belongs)
- Create merge request (proper workflow)
Your commits were never in danger. Set up the aliases and hooks above to make this less frequent.
Related reading:
- Git Branching Strategy: A Practical Case - The workflow this fix is part of
- GitLab CI/CD for Astro on Cloudflare - Branch protection in action with CI/CD