skip to content

Repositories and PR Workflow

How a repository is set up so that main stays trustworthy: fork versus clone, protection rules and rulesets, the pull request lifecycle, merge strategy, CODEOWNERS, and who holds which permission. Interviewers use this to see whether you can design a review process, not just follow one.

part ofGitHuboverview, primer and where to startread it →
on this pageshow

questions

29

In GitHub, what happens when you push straight to main if a rule requires a pull request?

level: juniorimportance: must knowfreq 68%

answer

  1. the decision happens on the server
  2. your local commits are unaffected
  3. the ref update is rejected
  4. remote error GH006, protected branch
  5. branch it and open a pull request

basics

~20 s

GitHub's server refuses the push. The remote rejects the ref update with a protected-branch error, nothing lands on main, and your local commits are untouched — you push a topic branch and open a pull request instead.

solid answer

~50 s

Branch protection lives on GitHub's side, not in your local Git. When the push reaches the server, GitHub evaluates the rules that target `main`; if a rule requires a pull request before merging, the ref update is rejected and the client prints `! [remote rejected] main -> main (protected branch hook declined)` along with a `remote: error: GH006: Protected branch update failed` line naming the reason. Nothing is lost locally — your commits are still on your local `main`. The normal recovery is to move them onto a topic branch (`git switch -c feature/x`), push that branch, and open a PR, which is the path the rule is forcing you onto. Whether anyone can push directly at all depends on the rule's bypass configuration; by default not even repository admins are exempt unless they are listed as bypass actors.

code

console · 8 lines
console
$ git push origin main
Enumerating objects: 5, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Changes must be made through a pull request.
To github.com:acme/payments-service.git
 ! [remote rejected] main -> main (protected branch hook declined)
error: failed to push some refs to 'github.com:acme/payments-service.git'

go deeper

for a junior

Recall that the rejection comes from GitHub, not your machine, and that nothing local is lost. Be able to describe moving the commits to a new branch and opening a pull request.

for a middle

Explain that GitHub evaluates rules targeting the ref when the push arrives, and that the reason line in the remote error tells you which rule fired — required PR, restricted pushes, signatures, or blocked force pushes.

for a senior

Show how you would unblock a stuck teammate without data loss, and how you would confirm from the repository configuration which rule rejected the push rather than guessing from the message.

for a principal

Own the policy stance: whether any direct-push path to main exists at all, who may bypass, and how a break-glass push is recorded and reviewed afterwards.

## Where the rule actually lives A protected branch is a **server-side** policy on GitHub, not a setting in your working copy. Your local Git happily creates commits on `main`, because your clone has no idea any policy exists. The check happens when `git push` sends the ref update to GitHub: the receiving side looks at every rule targeting `refs/heads/main` — a classic branch protection rule, one or more rulesets, or both — and either accepts or refuses the update. This is why a colleague can commit to `main` locally all day and only discover the policy at push time. ## What the rejection looks like The transport reports a per-ref failure, so the push fails atomically for that ref. The `remote:` lines are GitHub speaking. `GH006: Protected branch update failed for refs/heads/main` is the classic branch-protection code, followed by a human-readable reason such as `Changes must be made through a pull request` or `At least 1 approving review is required by reviewers with write access`. When the rule comes from a ruleset instead, the message names the ruleset and the specific rule that was violated. Either way, the exit status is non-zero, and Git's summary line is `error: failed to push some refs to ...`. ## Nothing is lost A rejected push changes nothing anywhere. The commits still exist in your local repository, reachable from your local `main`; the remote's `main` is byte-for-byte what it was. This matters because the panic reaction — `git push --force`, or `git reset --hard` to "clean up" — is exactly what destroys work. Force is useless here anyway: the rule rejects the update regardless of how it is expressed, and a separate rule normally blocks non-fast-forward pushes outright. ## The intended path Move the work onto a branch and open a pull request: 1. `git switch -c feature/rate-limit` — the branch now points at your commits. 2. `git switch main && git reset --hard origin/main` — put local `main` back in sync with the remote. 3. `git push -u origin feature/rate-limit`, then open the PR. The PR is where the rest of the policy gets applied: required approvals, required status checks, conversation resolution, and whatever else the branch's rules demand. Merging through the PR is a merge performed **by GitHub on the server**, which the rules explicitly permit; a direct push is what they forbid. ## Related rules you may hit instead "Require a pull request before merging" is only one of several rules that reject a push: - **Restrict who can push** (classic) / **restrict updates** (rulesets) — pushes are allowed, but only from listed people, teams or apps. - **Block force pushes** (`non_fast_forward` in rulesets) — normal pushes are fine, history rewrites are not. - **Restrict deletions** — `git push origin :main` is refused. - **Require signed commits** (`required_signatures`) — an unsigned commit in the pushed set is rejected. - **Lock branch** — the branch is read-only entirely, including for merges. Each produces its own reason line, so read the `remote: error:` text rather than guessing. ## Can an admin get through? Only if configured. Classic branch protection has a **"Do not allow bypassing the above settings"** checkbox; leave it on and admins are held to the same rules. Rulesets express the same idea as an explicit **bypass list** of roles, teams and GitHub Apps, optionally scoped to "for pull requests only" (they may open and merge PRs that skip a rule, but still cannot push directly). A repository where everyone believes "admins can always push" almost always has a bypass entry someone forgot about, and the bypass is recorded — rulesets surface it in rule insights and the organization audit log. ## Interview framing The point of the question is whether you understand that policy is enforced by the hosting platform at receive time, not by your Git client, and that a rejected push is a safe, reversible state. Say that, name the error you would read, and describe the branch-plus-PR recovery, and you have answered it fully.

  • Does the rule stop you from committing to main locally?
    No. Your clone knows nothing about GitHub's branch rules, so commits on a local `main` are created normally. Enforcement happens only when the ref update reaches GitHub, which is why the surprise always arrives at push time rather than commit time.
  • You are a repository admin — can you push directly anyway?
    Only if the configuration says so. Classic branch protection has a "Do not allow bypassing the above settings" toggle that holds admins to the rules; a ruleset instead names bypass actors explicitly. If neither grants you an exemption, your push is rejected exactly like anyone else's, and any bypass you do use is recorded.
  • How does the rejection differ when the rule comes from a ruleset rather than classic protection?
    The push is refused the same way, but the message identifies the ruleset and the specific rule that was violated rather than a single generic protected-branch reason. That is more useful for diagnosis, because several rulesets can target the same branch and you learn which one blocked you.

saying these in an interview costs you the question

  • Believes the rejected commits are lost or rolled back
  • Calls branch protection a local Git hook in the clone
  • Reaches for git push --force to get past the rule
  • Assumes repository admins are always exempt
  • Thinks the branch is read-only even for pull-request merges

context

open as a page

In a GitHub pull request description, what does the keyword "Closes #142" actually do?

level: juniorimportance: must knowfreq 62%

basics

~10 s

It links the pull request to issue 142 and closes that issue automatically when the pull request is merged into the repository's default branch. Merging into any other branch links but does not close.

open as a page

What are GitHub's repository access roles, and what can each one do?

level: juniorimportance: must knowfreq 70%

basics

~20 s

GitHub repositories have five access roles: Read (view and clone), Triage (manage issues and pull requests without pushing), Write (push code), Maintain (most repository settings, no destructive ones), and Admin (full control, including visibility, transfer and deletion).

open as a page

In GitHub, what does requiring branches to be up to date add to required status checks?

level: middleimportance: must knowfreq 64%

basics

~20 s

It forces the pull request branch to contain the base branch's newest commit before merging, so the required checks proved green on that exact combination. Without it, checks only prove the branch was green against an older base.

open as a page

In a GitHub CODEOWNERS file, which rule applies when several patterns match the same file?

level: middleimportance: must knowfreq 70%

basics

~20 s

The last matching line in the file wins, and only that line's owners apply. Ownership is never additive across rules, so an earlier specific rule loses to a later broad one — write general patterns first and specific ones last.

open as a page

Where does GitHub look for a CODEOWNERS file, and which branch's copy governs a pull request?

level: middleimportance: must knowfreq 50%

basics

~20 s

GitHub reads CODEOWNERS from the .github directory, the repository root, or docs — the first one it finds, in that order, and only one file is used. The copy that applies to a pull request is the one on the base branch it targets.

open as a page

In GitHub, why doesn't adding someone to CODEOWNERS by itself stop a pull request from merging?

level: middleimportance: must knowfreq 62%

basics

~10 s

CODEOWNERS on its own only auto-requests reviewers when a pull request is opened. Blocking a merge requires enabling required review from Code Owners on the target branch, through branch protection or a repository ruleset.

open as a page

In GitHub's merge queue, what is a merge group and what does CI actually test?

level: middleimportance: must knowfreq 40%

basics

~20 s

A merge group is a temporary branch GitHub creates under gh-readonly-queue containing the base branch plus the queued changes up to and including one entry. CI runs against that ref via the merge_group event, and only a green result lets the entries land.

open as a page

What does GitHub's merge queue prevent that required checks on a pull request cannot?

level: middleimportance: must knowfreq 46%

basics

~20 s

It prevents changes that were only ever tested apart from breaking the base once combined. The queue re-tests each pull request against the base plus everything landing ahead of it, in the exact order, and merges only if that combination is green.

open as a page

On GitHub, how do the squash, merge commit, and rebase merge buttons differ in what they leave on the base branch?

level: middleimportance: must knowfreq 80%

basics

~20 s

A merge commit keeps every PR commit and adds a two-parent commit. Squash and merge replaces them with one new commit. Rebase and merge replays them as new commits with new SHAs and no merge commit.

open as a page

In a GitHub organization, how is a member's effective permission on a repository determined?

level: middleimportance: must knowfreq 55%

basics

~20 s

GitHub takes the highest access from every source that applies: the organization's base permission, each team the member belongs to (including access inherited from a parent team), and any direct collaborator grant. Organization owners always hold admin.

open as a page

In a GitHub CODEOWNERS file, what can appear as an owner and what access must it have?

level: juniorimportance: should knowfreq 55%

basics

~20 s

Owners are GitHub usernames (@octocat), organization teams (@my-org/backend), or a user's email address. Every owner must have write access to the repository, and a team needs that access granted to the team itself, not only to its members.

open as a page

What is a draft pull request on GitHub, and what changes when you mark it ready for review?

level: juniorimportance: should knowfreq 46%

basics

~20 s

A draft pull request is a fully visible pull request that GitHub refuses to merge. Marking it ready enables the merge button and triggers the automatic reviewer requests, including code-owner requests, that GitHub holds back while it is a draft.

open as a page

In GitHub, what does the Require linear history branch rule actually prevent?

level: middleimportance: should knowfreq 42%

basics

~10 s

It rejects any commit with more than one parent landing on the branch. Practically, the merge-commit button is unavailable on that branch: only squash merge and rebase merge can produce commits GitHub will accept.

open as a page

In GitHub, what can repository rulesets do that classic branch protection cannot?

level: middleimportance: should knowfreq 50%

basics

~20 s

Rulesets layer: several can target one branch and all apply. They can be defined once at organization level across many repositories, target tags and pushes as well as branches, carry an explicit bypass list, run in a non-blocking evaluate mode, and are visible to anyone with read access.

open as a page

What does enabling auto-merge on a GitHub pull request do, and when is that option available?

level: middleimportance: should knowfreq 44%

basics

~20 s

Auto-merge tells GitHub to merge a pull request by itself, with a merge method you pick up front, as soon as every remaining requirement is satisfied. It requires the repository's Allow auto-merge setting and a pull request that is not yet mergeable.

open as a page

When would you use a GitHub deploy key instead of a token for a CI job's repository access?

level: middleimportance: should knowfreq 45%

basics

~20 s

Use a GitHub deploy key when a machine needs Git access to exactly one repository and nothing else. It is an SSH key attached to that repository, read-only unless you enable write, tied to no user account, and carrying no API access.

open as a page

If two GitHub rulesets and a branch protection rule target main, which rules apply?

level: seniorimportance: should knowfreq 40%

basics

~20 s

All of them. GitHub aggregates every rule from every applicable ruleset and from classic branch protection, and where they overlap the most restrictive setting wins. There is no first-match precedence and no way for one to relax another.

open as a page

A GitHub pull request touches an owned path but no code owner was requested. How do you debug it?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Work down the resolution chain: is the CODEOWNERS file in a valid location on the pull request's base branch, is the errors view clean, do the named owners have write access, does the pattern really match the path, is a later rule overriding it, and is the pull request still a draft.

open as a page

In a GitHub merge queue, what happens when a speculative merge group's checks fail?

level: seniorimportance: should knowfreq 36%

basics

~20 s

GitHub discards that group, removes the offending pull request from the queue and tells its author, then rebuilds the entries behind it without that change. Anything built speculatively on top of the failed group was based on a false assumption and is re-tested.

open as a page

Why would every entry in a GitHub merge queue time out with no checks reported?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Because nothing is producing results for the merge group. CI that only reacts to pull requests never runs on the temporary queue ref, so the checks required on the branch stay unreported until the check timeout evicts each entry.

open as a page

A merged GitHub pull request broke production — what does the Revert button on that pull request create?

level: seniorimportance: should knowfreq 40%

basics

~20 s

It creates a new branch and opens a new pull request whose diff undoes exactly what that pull request merged. Nothing is written to the base branch until that revert pull request passes the usual checks and is merged.

open as a page

What actually changes when a GitHub repository is switched from public to private?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Anonymous read stops and access falls back to organization base permission, teams and collaborators. Existing public forks are split into their own network and keep the code they already have, so anything published while the repository was public must be treated as permanently exposed.

open as a page

How would you decide who may bypass GitHub branch protection, and keep it auditable?

level: principalimportance: should knowfreq 30%

basics

~20 s

Start from nobody. Grant bypass to roles, teams or apps rather than individuals, scope it to pull requests where possible, keep the emergency path deliberately slow and visible, and review every recorded bypass — rule insights and the audit log — on a fixed cadence.

open as a page

How would you tune a GitHub merge queue's group size, concurrency, and wait time?

level: principalimportance: should knowfreq 27%

basics

~20 s

Size the settings from two measurements: merge attempts per hour and CI duration, then adjust for failure rate. Larger groups cut runs but multiply rework when red; more concurrent builds cut latency but cost runner minutes; wait time trades a little latency for fuller batches.

open as a page

How would you design least-privilege access for a GitHub organization with dozens of teams and hundreds of repositories?

level: principalimportance: should knowfreq 35%

basics

~20 s

Keep the organization base permission at read or none, grant everything else through nested teams that mirror the org, reserve admin to a small owners group, use outside collaborators for non-employees, and drive automation with app tokens rather than personal ones.

open as a page

In a GitHub CODEOWNERS file, why can't a ! negation line exempt a subdirectory from its owner?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

GitHub's CODEOWNERS syntax is gitignore-like but does not support negation with !, character ranges in square brackets, or escaping a leading # with a backslash. Carve out exceptions by adding a more specific rule below, since the last matching rule wins.

open as a page

In GitHub, what does archiving a repository do, and how does it differ from transferring it?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Archiving makes a GitHub repository read-only — no pushes, no new issues or pull requests, no setting changes — while leaving it visible, and it can be undone. Transferring moves the repository to another account or organization and redirects the old URL.

open as a page

How would you land a chain of five dependent GitHub pull requests without blocking the rest of your team?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Base each pull request on the previous one's branch so every diff stays small, land the bottom of the stack first, and prefer a merge method that preserves the parent's commits. GitHub retargets the children's base branches automatically as each parent merges.

open as a page