Git Workflow & Branching Governance Markdown Table Template
A branching table turns a team’s Git workflow from tribal knowledge into rules a contributor can follow on their first day. For each branch pattern it states where the branch starts, where its work ends up, who has to approve the merge, and which automated checks must pass first. It usually lives in CONTRIBUTING.md or the engineering handbook, and it is the document people open when they are unsure whether a fix goes against develop or main.
Edit This Template
Edit cells, add rows, or sort — the Markdown updates livePaste cells from Excel or Google Sheets into the first header to replace the table · Tab / Enter / arrows move · Shift+F10 opens row & column actions
When to Use This Table
The table is read at decision points: a developer creating a branch for an urgent production bug, a reviewer wondering whether one approval is enough, a release manager cutting a version, a new hire whose pull request was blocked by a check they have never heard of. It also doubles as the specification for the repository settings, since every row should correspond to a branch protection rule or ruleset that enforces it.
A useful table matches one coherent model. The template describes Git Flow — long-lived main and develop, plus feature/*, release/*, and hotfix/* — which suits software shipped as numbered versions with more than one release in support. Teams that deploy a single web app continuously usually do better with trunk-based development or GitHub flow, and Vincent Driessen, who described Git Flow in 2010, added a note in 2020 saying as much. The weak version of this table mixes models, or describes rules that the repository does not enforce.
What Goes in Each Column
| Column | Alignment | What to put here |
|---|---|---|
| Branch Name | left :--- | The branch or pattern in backticks: main, feature/*. On GitHub, patterns use fnmatch syntax in which * does not cross a slash, so feature/* misses feature/payments/refunds; add feature/*/* as a second pattern if nested names are allowed, or use a double-asterisk pattern, which matches any depth. Long-lived branches first, then short-lived ones. |
| Base Source | left :--- | The branch this one is created from. Short-lived branches always have one: features from develop, hotfixes from main. Long-lived branches are created once, so write — for main and main for develop, and describe what flows into them under Merge Target of the other rows. |
| Merge Target | left :--- | Where the finished branch merges, plus any back-merge. In Git Flow, release/* and hotfix/* both go to main and back to develop, otherwise the fix disappears in the next release. For long-lived branches, describe what the branch triggers, such as a production or staging deploy. |
| Required Approvals | center :---: | A number plus who counts, matched to something the platform can enforce: “2, including a code owner” maps directly to the required-approvals setting plus CODEOWNERS. Seniority labels like “senior engineer” only hold if a CODEOWNERS team encodes them. Centered because readers compare the counts down the column. |
| CI Automation Gates | left :--- | The checks that must be green before merging, named exactly as they appear in the pull request’s checks list, because required status checks match on that name. Mention post-merge automation separately, since a deploy that runs after merging is not a gate. |
How to Fill It In
- 1Choose the model before writing rows. If you deploy one version continuously, start from trunk-based development or GitHub flow; if customers run several released versions that you patch, Git Flow or maintained release branches fit better.
- 2Write one row per branch or pattern and check every pattern against real branch names with
git branch -r --list 'origin/feature/*'. - 3Copy check names from a recent pull request’s checks tab, not from the workflow file’s file name. Renaming a CI job without updating the protection rule leaves every pull request stuck waiting for a check that will never report.
- 4Enforce each row in the platform: on GitHub, a ruleset or branch protection rule with the approval count, Code Owners review, required status checks, and blocked force pushes. Note who is on the bypass list.
- 5Test the rules with a throwaway pull request that fails a check and has too few approvals, and confirm it cannot merge.
- 6Link the table from
CONTRIBUTING.mdand from the pull request template so contributors meet it before they open a branch.
Ready-to-Paste Variations
Trunk-based development
For teams deploying from main continuously. Short-lived branches and feature flags replace develop and release/*.
| Branch | Created from | Merges into | Lifetime | Gates |
| :--- | :--- | :--- | :---: | :--- |
| `main` | — | Deploys to production on every merge | Permanent | Merge queue; full test suite |
| `<user>/<short-topic>` | `main` | `main` (squash) | Hours to 2 days | Lint, unit tests, 1 approval |
| `revert-*` | `main` | `main` | Minutes | Unit tests; 1 approval, any maintainer |Maintained release branches
For libraries or on-premise products that patch several versions at once. Fixes land on main first and are cherry-picked backwards.
| Branch | Created from | Receives | Approvals | Gates |
| :--- | :--- | :--- | :---: | :--- |
| `main` | — | All feature work and fixes | 1 | Full test matrix |
| `release/3.x` | `main` at `v3.0.0` | Cherry-picked fixes only | 2 | Full test matrix, API compatibility check |
| `release/2.x` | `main` at `v2.0.0` | Security fixes only | 2, including security team | Full test matrix |From policy to GitHub ruleset settings
Maps each policy column to the setting that enforces it, which is useful when auditing whether a repository matches its own documentation.
| Policy in the table | GitHub ruleset setting |
| :--- | :--- |
| Merge only through pull requests | Require a pull request before merging |
| 2 approvals, one from the owning team | Required approvals: 2, plus Require review from Code Owners |
| New commits need a fresh review | Dismiss stale pull request approvals when new commits are pushed |
| CI gates must pass | Require status checks to pass |
| No history rewriting | Block force pushes |Common Mistakes
Listing
developas the base ofmain, orfeature/*as the base ofdevelop.Base Source is where a branch is created from. In Git Flow,
developis created frommainonce, andmainonly receives merges fromrelease/*andhotfix/*. Show those flows in the Merge Target column of the short-lived branches.Merging a release branch into
mainand stopping there.Merge
release/*back intodevelopas well, or version bumps and last-minute fixes are lost. If a release branch is open when a hotfix lands, Git Flow merges the hotfix into that release branch instead ofdevelop.Writing approval rules the platform cannot check.
“2 senior engineers” is a social rule unless a CODEOWNERS entry assigns those paths to a senior team and the ruleset requires Code Owners review. Write the enforceable version and keep the intent in a footnote.
Allowing feature branches to live for weeks.
Long-lived branches turn into painful merges whichever model you use. Set a target lifetime, merge incomplete work behind a feature flag, and rebase or merge from the base branch daily.
Related Markdown Table Templates
Browse all 50 templatesCI/CD Pipeline Stages & Automation Gates
Document multi-stage CI/CD workflows across linting, unit testing, security scanning, container builds, and production deployments.
Conventional Commits & Semantic Releases
Conventional Commit specifications mapping commit type prefixes to SemVer release impacts, changelog categories, and example formats.
Software Release Changelog & Notes
Semantic versioning changelog recording release versions, deployment dates, feature highlights, bugfixes, and breaking updates.
Git Workflow & Branching Governance: Frequently Asked Questions
Should we use Git Flow or trunk-based development?
Ask how many versions you support at once. A web service with one production version gets little from develop and release/* and pays for them in merge overhead. Mobile apps, desktop software, and libraries with supported older versions benefit from release branches.
Squash, merge commit, or rebase?
A common split is to squash feature branches so each pull request becomes one commit, and to use merge commits (--no-ff) for release and hotfix merges into main, so each release is a visible point in history. Pick per branch and record it in this table.
Do hotfixes need the same approvals as features?
Usually fewer approvers but the same checks. Speed should come from a smaller diff and a named on-call approver, not from skipping tests. If admins may bypass rules in an emergency, say so in the table and review every bypass afterwards.
How do I document branch naming beyond the prefix?
Add a line under the table with the full pattern and an example, such as feature/<ticket>-<slug> and feature/PAY-142-refund-api. Including the ticket key lets trackers such as Jira link the branch to its issue automatically.