The “Oh Crap” Moment

You know that sinking feeling when you’ve been coding for an hour, making great progress, and then you try to push your changes?

git push origin main

And GitLab/GitHub slaps you with:

remote: GitLab: You are not allowed to push code to protected branches on this project.

Then it hits you. You never created a feature branch. You’ve been committing directly to main this whole time.

I’ve done this more times than I’d like to admit. Usually it’s Monday morning, coffee hasn’t kicked in yet, and I dive straight into fixing a bug without thinking. Then… this.

The good news? Your commits are completely safe, and this is actually easy to fix. Takes about 2 minutes once you know what to do.

Why This Happens

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?

Here’s what your Git history looks like right now:

gitGraph
  commit id: "Remote main"
  commit id: "Your commit 1" type: HIGHLIGHT
  commit id: "Your commit 2" type: HIGHLIGHT
  commit id: "Your commit 3" type: HIGHLIGHT

Your local main branch is ahead of the remote main branch by 3 commits (or however many you made). The remote repository won’t let you push because main is protected.

What we need to do:

  1. Move your commits to a new feature branch
  2. Reset your local main back to match the remote
  3. Push the feature branch instead
  4. Create a merge request (the proper way)

Don’t worry - your commits won’t be lost. We’re just moving them to the right place.

The Fix: Move Commits to Feature Branch

Here’s the recovery process. I’m assuming you’re currently on the main branch with uncommitted work (or committed work that can’t be pushed).

Step 1: Check Your Current Status

First, let’s see what we’re working with:

git status
git log --oneline -5

This shows:

  • What branch you’re on (probably main)
  • 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 (a1b2c3d, e4f5g6h, i7j8k9l) are what we need to move.

Step 2: Create Feature Branch From Current Position

This creates a new branch right where you are now, preserving all your commits:

git checkout -b feature/user-authentication

Replace feature/user-authentication with whatever makes sense for your changes. Follow your team’s branch naming convention.

What just happened:

  • Created new branch called feature/user-authentication
  • It contains all your commits
  • You’re now on the new branch (not on main anymore)

Step 3: Verify Feature Branch Has Your Commits

Double-check everything made it:

git log --oneline -5

You should see all your commits. Perfect. They’re safe on the feature branch now.

Step 4: Switch Back to Main and Reset It

Now we need to fix main by resetting it to match the 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

What git reset --hard origin/main does:

  • Resets your local main branch to exactly match origin/main (the remote)
  • Discards any commits that were only on local main
  • Safe because: We already saved those commits on the feature branch
Important: Only Run This on Main

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

Check that main is now clean:

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

Perfect. Main is back to normal.

Step 6: Push Feature Branch and Create Merge Request

Switch back to your feature branch and push it:

# 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

What -u origin feature/user-authentication does:

  • Creates the branch on the remote repository
  • Sets up tracking so future git push commands work without arguments
  • -u is short for --set-upstream

Step 7: Create Merge/Pull Request

Now create a merge request (GitLab) or pull request (GitHub) through your platform’s UI, or use the 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"

Or just click the link that appears in your terminal after pushing - both platforms usually show a convenient URL to create the MR/PR.

Visual: What We Just Did

Here’s what your branches look like now:

gitGraph
  commit id: "Remote main (origin/main)"
  branch feature/user-authentication
  checkout feature/user-authentication
  commit id: "Your commit 1"
  commit id: "Your commit 2"
  commit id: "Your commit 3"
  checkout main
  • main branch: Back in sync with remote, clean
  • feature branch: Contains all your commits, ready for merge request
  • Your work: Safe, in the right place, following proper workflow

Verification Checklist

Before you close this tab, verify everything worked:

  • git status on main shows “up to date with origin/main”
  • git log on main doesn’t show your recent commits
  • git checkout feature/user-authentication switches to feature branch
  • git log on feature branch shows all your commits
  • Feature branch exists on remote (check GitLab/GitHub UI)
  • Merge request is created and shows your changes

If all those check out, you’re golden. Grab another coffee - you’ve earned it.

Preventing This (Because We All Forget)

Here are some tricks to make sure this doesn’t happen again (or at least less often):

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

Now every time you try to commit on main, you’ll get a warning.

Conclusion

Committing to main instead of a feature branch happens to everyone. Branch protection catching it is actually the system working correctly - it’s there to save you from yourself.

The recovery process is straightforward:

  1. Create feature branch from current position (saves commits)
  2. Reset local main to remote (cleans up main)
  3. Push feature branch (gets your work in the right place)
  4. Create merge request (follows proper workflow)

Your commits were never in danger. You just needed to move them to the right branch.

Set up some of those prevention tricks (aliases, hooks, shell prompt), and this’ll happen less often. And when it does happen again (because it will), you’ll know exactly what to do.

Now go create that merge request and get your code reviewed the proper way.

Related reading: