T19-L03 · Share & review code · Level 3 Builder · 15-minute read, plus 90 minutes of doing

Almost what you need

The qPCR protocol from T19-L02 · Work on a Shared Gitea Repository From Your Computer With OpenCode is now the one five people in the lab follow. Last week a change went straight in with a typo in a dilution, and two plates were wasted before anyone noticed. Everyone could push to main, so nobody read a change before it counted.

In this book you close main to direct pushes, so every change comes as a pull request: a proposal that a colleague reads line by line, comments on, and approves before it goes in. You record the problem as an issue and let the merge close it. You are at Level 3 Builder: colleagues depend on this repository, and a change that reaches main unread reaches all of them.

After this you can

  • Protect main so that nobody, including you, can push to it or merge without an approval.
  • Record a problem as an issue, and link the change that fixes it.
  • Make a change on a branch with OpenCode and open a pull request for it.
  • Review a pull request: comment on a line, request changes, approve.
  • Merge the pull request, and let the merge close the issue.

Before you start

  • Books: T19-L02 · Work on a Shared Gitea Repository From Your Computer With OpenCode. Your repository is on your computer, and a colleague has Write access. In the example you are Mia (the owner) and Jonas is the colleague who reviews; steps 3, 6, 7 and 9 happen on Jonas's screen.
  • Owner or Admin rights on the repository, to change its settings.
  • About 105 minutes: 15 to read, 90 to do, most of it waiting for and doing the review with your colleague.

Last verified 25 September 2026. Branch protection, pull requests, reviews and issues are built into every Gitea, free, including gitea.com. Details for experts at the end of this book.

The idea in one page

  • main is what everyone uses. Protect it: no direct pushes, and no merge without an approval.
  • A branch is a side copy for one change. You work there; main stays untouched.
  • A pull request asks to merge a branch into main: merging copies the branch's changes into main. Its page shows the change in red and green, the discussion, the reviews and whether it may be merged.
  • A review ends in one of three answers: Comment, Approve or Request changes. With the rule from step 1, a request for changes blocks the merge until that reviewer approves. Official approvals are those from people with write access.
  • An issue records a problem. Fixes #1 in a pull request closes issue #1 when the pull request is merged.
  • The reviewer reads the change, not the summary. OpenCode's answer says what it meant to do; the Files Changed tab shows what it did.

Watch it once (75 seconds)

No sound. A yellow dot points at each button while the caption explains it. Read the tour as text (opens in a new tab).

Set up (step 1, once per repository)

Step 1: Protect main. In the repository, click Settings, then Branches, then Add New Rule. Fill in:

  • Protected Branch Name Pattern: main
  • Push: Disable Push
  • Required approvals: 1
  • Tick Dismiss stale approvals (a new commit needs a fresh approval), Block merge on rejected reviews (Request changes stops the merge) and Administrators must follow branch protection rules (you, the owner, too)

Click Save Rule.

The Add New Rule form, shortened: pattern main, Disable Push selected, Required approvals 1, Dismiss stale approvals ticked, Block merge on rejected reviews and Administrators must follow branch protection rules ticked, and the Save Rule button

You should see: main in the list of protected branches.

Worked example: a rule for failed controls

Structure it

Step 2: Say what you want in one sentence.

When someone changes the protocol, I want a colleague to read and approve the change before it counts, instead of finding mistakes on the next plate.

Understand it

Step 3: Record the problem as an issue. (Jonas's screen.) Jonas found something the protocol doesn't cover. In the repository he clicks Issues, then New Issue, writes a title and what happened, and clicks Create Issue.

The New Issue form: title What do we do when a no-template control shows a peak?, and a description of run 24 September, well A2, a melt peak at 72 °C and a Cq of 33

You should see: the issue with a number, here #1, and a green Open label.

Issue #1, open, with Jonas's description

Choose tools

A branch and a pull request for every change, Jonas as reviewer, and OpenCode for the change on Mia's computer.

Build it: propose the change

Step 4: Make the change on a branch. In OpenCode, in the project's session, send:

Get the latest changes from Gitea. Then create a branch ntc-rule from main and switch to it.
In protocol.md, under "## Checks", add the bullet: "- If a no-template control shows a melt peak, repeat the run with fresh primer dilutions." Change nothing else.
Commit it with the message "Add what to do when an NTC shows a peak (fixes #1)" and push the branch ntc-rule to Gitea.
If Git reports a conflict, stop: don't resolve it yourself. Show me both versions of the lines and who wrote each.

OpenCode's answer: pulled, created the branch ntc-rule from main, added one bullet under Checks, committed with the message Add what to do when an NTC shows a peak (fixes #1), and pushed it as a new branch ntc-rule

You should see: OpenCode says it pushed a new branch ntc-rule. Name branches after what they change. If OpenCode says the issue closes when you open the pull request: it closes at the merge (step 11).

Step 5: Open the pull request. On the repository page Gitea shows a green bar for a while after a push. Click New Pull Request in it (or, later, Pull Requests → New Pull Request and choose the branch).

A green bar on the repository page: You pushed on branch ntc-rule now, with a New Pull Request button

Check the two boxes at the top read merge into: …:main and pull from: …:ntc-rule. Give it a title and a description that starts with Fixes #1. and says what the reviewer should check. On the right, click Reviewers and choose Jonas. Click Create Pull Request.

The Comparing changes page: merge into main, pull from ntc-rule, the title, the description Fixes #1 followed by what to check, and jonas under Reviewers

You should see: the pull request with its own number (here #2; issues and pull requests share the numbers), and a red merge box: 0 of 1 official approvals granted. Don't click the red Create merge commit (When checks succeed) button: it would schedule the merge for later. Wait for the review. There is no key available to sign this commit is harmless.

Pull request #2, open, with its red merge box: This pull request doesn't have enough required approvals yet. 0 of 1 official approvals granted

Build it: review

Step 6: Comment on a line. (Jonas's screen.) Gitea tells Jonas about the review request with the bell at the top right (and by email, if set up). He opens the pull request and clicks Files Changed. He moves the mouse over the green line, clicks the blue + at its start, writes his comment, and clicks Start review: the comment waits until he sends the whole review, instead of going out alone.

Files Changed with the new green line 24 and an open comment box under it: What counts as a peak? Every NTC has a small bump. Please give a number, for example a Cq below 35, so everyone decides the same way. Buttons Start review, Add single comment and Cancel

Step 7: Request changes. (Jonas's screen.) Jonas clicks Review at the top right, writes one sentence for the whole review, and clicks Request changes.

The Submit review box with the text The rule is the right idea, but it needs a threshold before we can use it, and the buttons Approve, Comment and Request changes

You should see (as Mia, on the pull request): jonas requested changes, his comment on the line, and a merge box with two red lines.

The pull request conversation: jonas requested changes, his comment on line 24, and the merge box saying there aren't enough approvals and that changes were requested by an official reviewer

Step 8: Answer the review on the same branch. Mia agrees with Jonas and sends in OpenCode:

Stay on the branch ntc-rule. In protocol.md, change the NTC line to: "- If a no-template control has a Cq below 35 or a melt peak above 70 °C, repeat the run with fresh primer dilutions." Change nothing else.
Commit it with the message "Give the NTC rule a threshold: Cq below 35 or a peak above 70 °C" and push the branch.
If Git reports a conflict, stop: don't resolve it yourself. Show me both versions of the lines and who wrote each.

OpenCode's answer: still on ntc-rule, replaced the NTC line, one insertion and one deletion, committed and pushed; it notes that the open pull request #2 now contains the change

You should see: the push went to ntc-rule. You don't open a new pull request: the new commit appears in pull request #2 by itself.

Step 9: Approve. (Jonas's screen.) Jonas opens Files Changed again. His comment is now marked Outdated, because the line changed.

Files Changed after the fix: line 24 now reads If a no-template control has a Cq below 35 or a melt peak above 70 °C, and Jonas's comment below it is marked Outdated

He checks the new line, clicks Review, writes a short note and clicks Approve.

The Submit review box with Clear now. Approved. and the Approve button

You should see: jonas approved these changes and a blue merge button with This pull request can be merged automatically: Gitea's words for no conflicts, ready to merge. Nothing merges until someone clicks.

The pull request conversation: Mia added a commit, jonas approved these changes, and the merge box with a blue Create merge commit button

Build it: merge

Step 10: Merge and delete the branch. Mia clicks Create merge commit. Gitea fills in a message that names the pull request and Reviewed-by: jonas. She ticks Delete Branch "ntc-rule" and clicks Create merge commit again.

The merge form: the title Merge pull request 'Add what to do when an NTC shows a peak' (#2) from ntc-rule into main, the text Reviewed-by jonas, and Delete Branch ntc-rule ticked

You should see: a purple Merged label, and mia deleted branch ntc-rule.

Pull request #2 with the purple Merged label, and below it mia merged commit into main and mia deleted branch ntc-rule

Step 11: Check the issue. Open Issues, then the closed issue #1.

Issue #1 with a red Closed label; below Jonas's description, mia referenced this issue from a commit, mia referenced a pull request that will close this issue, and mia closed this issue

You should see: Closed, and the pull request that closed it. Anyone who reads the issue later finds the fix and its review.

Step 12: Update main on your computer. In OpenCode, send:

The pull request is merged on Gitea. Switch to main, get the latest changes from Gitea, and delete my local branch ntc-rule. Then show me the NTC line in protocol.md and the last two commits.

OpenCode's answer: switched to main and pulled, deleted the local branch ntc-rule, shows the NTC line with the threshold, and the last two commits: the merge of pull request #2 and Give the NTC rule a threshold

You should see: the final NTC line and the merge on top. OpenCode may add that the branch "is still on Gitea" and offer to delete it: not needed. It's looking at an old local note; Gitea deleted the branch in step 10, as the Commits page shows: 1 Branch.

Gitea's commit list: the merge of pull request #2 on top, then Give the NTC rule a threshold

Company: the onboarding checklist

Aylin (IT) and Tom (HR) protect main of onboarding the same way.

  • Step 3: a new colleague reports an issue: My mail account didn't exist on day 1.
  • Steps 4 and 5: Aylin opens a pull request Create mail accounts five days ahead (fixes #1) and asks Tom to review.
  • Steps 6 to 9: Tom asks for the name of the ticket queue; Aylin adds it; Tom approves.
  • Steps 10 and 11: the merge closes the issue.

What goes wrong

"Not allowed to push to protected branch main." Symptom: a push from OpenCode ends with this line and pre-receive hook declined. Fix: that's step 1 doing its job. Ask OpenCode: "Move my last commit from main to a new branch <name>, make my main match Gitea again, and push the branch." Then open a pull request.

The merge button stays red. Symptom: 0 of 1 official approvals granted, or changes requested. Fix: the reviewer hasn't approved yet, or asked for changes. Answer the comments on the same branch (step 8), then click the round-arrow icon next to the reviewer's name under Reviewers to ask again.

The approval disappeared. Symptom: Jonas had approved, then you pushed a small fix, and the merge box is red again. Fix: expected: Dismiss stale approvals removes an approval when the change changes. Ask for a quick second look.

"This branch is out-of-date" or conflicts. Symptom: main changed while the pull request was open. Fix: if the merge box offers Update branch, click it. If it reports conflicts, in OpenCode: "On the branch <name>, merge the latest main from Gitea and push the branch. If Git reports a conflict, stop and show me both versions." Decide with the colleague who changed main (T19-L02, step 16).

The review only read the description. Symptom: an approval within a minute of a long change. Fix: a reviewer reads Files Changed, line by line. If a pull request is too long to read in ten minutes, split it into smaller ones.

Wrong direction. Symptom: the pull request wants to merge main into your branch. Fix: the top boxes must read merge into: main and pull from: your branch. Close it and open a new one the right way round.

Do it yourself (90 minutes)

  1. Set up (10 min): step 1 on your repository. Then ask OpenCode to push a one-word change straight to main. It must be refused; undo it with the first fix in What goes wrong.
  2. Structure it (5 min): your sentence: "When someone changes ___, I want ___ to read it before it counts, instead of ___."
  3. Understand it (10 min): your colleague records a real open question as an issue (step 3).
  4. Choose tools (5 min): a branch name that says what the change is; your colleague as reviewer.
  5. Build it (60 min): steps 4 and 5 for the fix, then a real review with at least one line comment and one round of changes (steps 6 to 9), then merge (steps 10 to 12).

Exit check

A protected main, an issue, and a merged pull request that fixes it. It passes when the pull request shows at least one line comment, one Request changes, a new commit answering it and an Approve; the issue was closed by the merge; and a direct push to main from your computer is refused.

Rule to remember

Nothing reaches main until a second person has read the change itself.

Details for experts

Checked on 25 September 2026. You don't need this to follow the book; it's for setting things up or answering a colleague.

WhatDetailsWhat it means for you
VersionsGitea 1.27.2, OpenCode Desktop 2.0.16 with the free Space Bunny model.Settings may move slightly between Gitea versions; the names stay.
Protection ruleAlso: protected file patterns, a push allowlist, signed commits, Block merge if pull request is outdated, required status checks (automatic tests), and Restrict approvals to named reviewers.Status checks connect to automatic tests: Level 4. Restrict approvals when only some people may approve, for example a lab manager for SOPs.
Merge stylesThe arrow next to Create merge commit offers Rebase then fast-forward, Rebase then create merge commit, Create squash commit and Fast-forward only.Squash turns the branch's commits into one: a tidy history, but the separate steps are gone from main (the review stays on the pull request). A merge commit keeps them.
Closing keywordsFixes, Closes, Resolves (and a few more) followed by #number, in the pull request description or a commit message.The issue closes when the pull request is merged into the default branch.
Scheduled mergeWhile the rules aren't met, the button reads Create merge commit (When checks succeed) and schedules an automatic merge for later.It's meant for automatic tests (Level 4). After a review, use the normal blue button.
WIPA title starting with WIP: marks a pull request as work in progress and blocks its merge.Open a draft early, ask for comments, remove WIP: when it's ready.
GitHubSettings → Rules → Rulesets (or Branches → branch protection): Require a pull request before merging, Required approvals, Dismiss stale pull request approvals, Do not allow bypassing the above settings. Reviews: Files changed → Review changes (Submit review in the newer view) → Comment, Approve, Request changes.Same ideas and keywords; GitHub offers draft pull requests in place of WIP:. For private repositories, GitHub's protection rules need a paid plan.

Further reading and tools