DEV Community

Wen Ping
Wen Ping

Posted on Edited on Originally published at anguskit.com

How to protect the Git main branch: turn on branch protection, then merge through a pull request

This article explains how to stop someone from pushing code straight to main. After reading it, you can open Settings → Branch Protection, turn on Require pull request reviews and Include administrators, leave Allow force pushes and Allow deletions off, push once and see the refusal, then merge the change through a pull request.

A pull request is a request to merge one branch into another. AngusGit calls it a pull request. The sidebar page is Pull Requests. GitLab calls the same thing a merge request, and GitHub calls it a pull request. You need permission to manage the repository before you can set branch protection.

The examples use the online repository xlliu/test-pr-example. The clone URL is https://git.anguskit.com/xlliu/test-pr-example.git. Merged pull request #1 feat: 增加 VIP 折扣与优惠券策略 merged feature/add-discount into main. The change is in CouponPolicy.java, OrderController.java, OrderService.java, and PriceCalculator.java, 34 lines added in total. When you test that a direct push is refused, create a new branch. Do not overwrite feature/add-discount.

Before protection you can push straight to main. After protection the change goes through a new branch, a pull request, and a review, then a merge

1. What gets blocked after you add protection

Know the result first. Each switch below is easier to understand once you know what it blocks. The table uses the settings this article recommends. The merge card is the card on the pull request page that says whether the pull request can be merged.

Action Result after protection What the terminal or page says
Run git push origin main locally Refused Branch 'main' is protected: direct push is not allowed, please open a pull request
git push --force origin main Refused Branch 'main' is protected: force push is not allowed
git push origin --delete main Refused Branch 'main' is protected and cannot be deleted (deleting it on the website is refused too)
Edit a file on main in the browser You start a new branch The Edit button becomes Create branch & PR
Nobody has approved the pull request yet Cannot merge The merge card says This pull request is not ready to merge
One approval, and there is no conflict Can merge The merge card says This pull request is ready to merge, and you can click Merge pull request

Branch protection applies to the branch. Pushing a tag is not covered by this rule.

2. Add a protection rule for main

  1. Open branch protection Open the repository, go to Settings, and choose Branch Protection on the left. The heading is Branch protection rules. The description is "Protect branches from accidental changes".
  2. Choose the branch Click Add rule. The dialog title is Add branch protection rule. Under Branch name, choose main.
  3. Turn on the switches you need Set them as in the table below. Leave every switch that is not listed at its default, which is off.
  4. Save Click Add rule. Back on the list, the rule has a Protected badge. Open the repository Branches tab. The main row is marked protected.
Switch Setting in this article What it decides
Require pull request reviews On. Set Required approving review count to 1 How many approvals are required before merge. A direct push to main is refused only after you turn this on. If you leave it off, people can still push to main and skip the pull request
Include administrators On When this is off, a repository administrator is outside the rule: they can push straight to main, and they can merge a pull request with no approval. On a team of one or two people, this is the usual reason protection looks on and still does not block anyone
Require status checks Leave off for now Requires selected checks (a pipeline or a security scan, for example) to report success before merge. If the repository has no check that reports a result, the merge card keeps waiting
Require security check to pass Leave off for now Requires the security check to pass before merge. If the repository has no security scan yet, the merge card keeps waiting
Restrict who can push Leave off for now Limits push on this branch to chosen users and teams. Require pull request reviews already does what this article needs
Allow force pushes Leave off When this is on, someone can rewrite the history of main with --force
Allow deletions Leave off When this is on, main can be deleted

Allow force pushes and Allow deletions sit in a danger group. The page says "Danger zone: the switches below weaken branch protection, enable with caution". Both switches must stay off. If they are already on when you open the dialog, turn them off before you save. Otherwise main can still be force-pushed or deleted.

Add branch protection for main on xlliu/test-pr-example: 1 required review, include administrators, and leave force push and deletion off

3. Check it: a direct push is refused

After you save the rule, try a push yourself so you know it is really on. Commit a small change on your local main, then push:

git switch main
echo "test" >> README.md
git commit -am "test: direct push"
git push origin main
Enter fullscreen mode Exit fullscreen mode

The terminal shows this line:

 ! [remote rejected] main -> main (Branch 'main' is protected: direct push is not allowed, please open a pull request)
Enter fullscreen mode Exit fullscreen mode

That line means protection is on. The refused commit is still on your local main. The next section moves it onto a new branch and merges it with a pull request.

If the push succeeds, check two things in the table above: Require pull request reviews is on, and if you are a repository administrator, Include administrators is on.

4. Use a pull request and merge the change into main

  1. Put the change on a new branch From the current state, create a local branch named fix-readme and push it. The new branch is not protected, so the push succeeds. Then move local main back to the remote, so the next push is not refused again. reset --hard drops unsaved work on local main. Before you run it, confirm that the change is already on fix-readme.
  2. Open a pull request In the browser, open the repository Branches tab. On the fix-readme row, click Compare with default branch, then Create Pull Request. The repository and branches are filled in. You can also open Pull Requests in the sidebar, click New pull request, set Source branch/tag to fix-readme and Target branch to main. Write what changed in Title, write why in Description, and click Create Pull Request.
  3. Ask a colleague to review The merge card says This pull request is not ready to merge because the review is not finished. Ask a colleague to open this request under Review in the sidebar, read it, and submit Approve. How to read the diff and write comments is in the code review article linked at the end.
  4. Merge After the approval count reaches 1 and there is no conflict, the merge card changes to This pull request is ready to merge. The author or a maintainer clicks Merge pull request, chooses a merge method, and clicks Confirm merge. After the message Pull request merged successfully, main contains this change.

The commands for the first step move the local commit onto the new branch:

git switch -c fix-readme
git push -u origin fix-readme
git switch main
git reset --hard origin/main
git switch fix-readme
Enter fullscreen mode Exit fullscreen mode

The request that already finished in the example repository is #1. Next to the title on the conversation page is Merged. The subtitle says it wants to merge feature/add-discount into main. The file changes are the four Java files above. On a request you just opened, before anyone approves, the merge card stays on This pull request is not ready to merge. After the approval count is high enough and there is no conflict, it becomes This pull request is ready to merge. Only then can you click Merge pull request.

Pull request #1 on xlliu/test-pr-example is merged. Source branch feature/add-discount, target branch main

After the merge, update your local copy so main matches the remote:

git switch main
git pull
Enter fullscreen mode Exit fullscreen mode

5. When the merge card says the branch must be up to date

After you turn on Require status checks, the rule gains Require branches to be up to date. When that is on, the source branch must contain the latest commits from main before you can merge. The code you merge has been checked against the latest main.

If someone else merges into main while your pull request is open, the merge card asks you to update the branch first. Take the new commits from main onto your branch locally, then push:

git switch fix-readme
git fetch origin
git rebase origin/main
git push --force-with-lease
Enter fullscreen mode Exit fullscreen mode

fix-readme is not protected, so a force push is allowed here. --force-with-lease is safer than --force: if someone else pushed new commits to the remote fix-readme without your knowledge, it refuses to overwrite them. If you prefer not to rebase, replace the third line with git merge origin/main and the last line with a normal git push. The branch still contains the latest commits from main.

6. When the rule does nothing, or the merge stays blocked

What you see Why What to do
A direct push to main succeeds Require pull request reviews is off, or you are a repository administrator and Include administrators is off Go back to Settings → Branch Protection and turn on the matching switch
The merge card keeps saying status checks have not passed Require status checks is on, but no pipeline or scan in the repository reports that check Turn this off for now. After the check really runs, put the name it shows on the pull request into Required status check contexts
The merge card says the review is not finished, but your colleague already approved New commits were pushed after the approval, and the old approval is marked Needs re-review Ask your colleague to read the new change and submit Approve again
The merge card says you must resolve conflicts first Someone changed the same code on main On the pull request page, click Resolve conflicts online, or follow the commands in section 5 locally and push again
Branch 'main' is protected: force push is not allowed You used --force on main Do not force-push main. Put the commits you need to rewrite on a new branch and open a pull request
On the website you cannot click Edit. The button is Create branch & PR The current branch is protected. This is the expected result Enter a branch name, click Create & edit, and after you save, click Create Pull Request in the banner

Frequently asked questions

Can one rule protect several branches?

One rule matches the single branch you pick under Branch name. To protect a branch such as release, add another rule for it.

What is the difference between branch protection and Require code review under Settings → Pull Requests?

Branch protection applies only to the branch you picked, and it refuses the push itself. The switch in that dialog is Require pull request reviews. Require code review under Settings → Pull Requests applies to pull requests for every branch in the repository. It sets how many people must approve, and who is asked to approve. It does not block a push. If both places set a number of approvals, the number on the branch protection rule is the one that counts. To cover only main, branch protection is enough.

Should I turn this on if I am the only person maintaining the repository?

Yes. At least leave Allow force pushes and Allow deletions off, so a mistake cannot rewrite history. With only one person there is nobody else to review, so you can leave Require pull request reviews off until a second person joins.

Can I require a specific person to review?

Branch protection sets how many people must approve. It does not set who they are. To assign reviewers by directory, use a CODEOWNERS file together with branch protection.

Summary

Protecting main comes down to three switches: turn on Require pull request reviews and set the count, turn on Include administrators, and leave force push and deletion off. After you save, push once yourself and look for direct push is not allowed before you go on. From then on, every change follows the same path: a new branch, a pull request, an approval, and a merge. Every change on main has been read by someone, and there is a record of it.

Conclusion: Under repository Settings → Branch Protection, turn on Require pull request reviews for main (set the count) and Include administrators, and leave Allow force pushes and Allow deletions off. That blocks a direct push and sends the change through a pull request.

Related articles

Top comments (0)