π₯ Team Workflows
In class you were a team of one. The moment a second person touches your repository, whether a classmate, a client, or future-you on a second laptop, it helps to agree on how work flows into main. This page covers the habits and GitHub features teams use to do that calmly.
π§ Good news first
The workflow you used in Lesson 6 (branch β push β pull request β Deploy Preview β merge) is the workflow most small teams use. Everything here builds on it; nothing replaces it.
On This Page
π GitHub Flow
GitHub Flow has one long-lived branch, main, which is always working and always deployable. Every change, however small, happens on a short-lived branch and reaches main through a pull request.
- Start from an up-to-date main:
git switch main,git pull. - Branch with a descriptive name:
git switch -c add-gallery. - Commit in small steps and push the branch early:
git push -u origin add-gallery. - Open a pull request as soon as you want feedback. It can be a draft.
- Review and check: teammates comment, the Netlify Deploy Preview shows the real result, automated checks run.
- Merge, then delete the branch. Netlify deploys
main. Everyone runsgit switch main+git pullbefore their next branch.
π‘ Deploy previews are free; production deploys aren't
On Netlify's credit-based free plan, deploy previews don't use credits but every production deploy (every merge to main) does. Grouping related small fixes into one pull request is kinder to your monthly allowance than merging ten one-line PRs.
π€ Collaborators & Forks
Collaborators (people you trust)
Repository β Settings β Collaborators β Add people. Once they accept the email invitation they can clone, push branches, and merge pull requests. This is the right choice for a classmate on a group project or a co-owner of a site.
Forks (everyone else)
Anyone can Fork a public repository into their own account, push to their copy, and open a pull request back to yours. You decide what gets merged. This is how open source works; Practice Project 6 walks you through it.
Organizations (free for public work) let a group own repositories together and manage people in teams. Useful once you have more than a couple of shared repos.
π‘οΈ Protecting main
"Main is always deployable" is easier to keep when GitHub enforces it. Go to Settings β Rules β Rulesets β New ruleset β New branch ruleset (older repositories may show Settings β Branches β Add branch protection rule instead), target the default branch, and turn on:
- Require a pull request before merging: nobody, including you, can push straight to
main. Optionally require one or more approvals. - Require status checks to pass: the PR can't be merged until checks (like the HTML check below, or the Netlify deploy) are green.
- Block force pushes: history on
maincan't be rewritten.
On GitHub's free plan these rules apply to public repositories; enforcing them on private repositories needs a paid plan. Wording and menus move occasionally, so if a label differs, look for "Rules" or "Branches" in Settings.
π Pull Requests & Code Review
(or draft)"] --> B["Checks run +
Deploy Preview"] B --> C{"Review"} C -->|"Request changes"| D["Push more commits
to the same branch"] D --> B C -->|"Approve"| E["Merge"] E --> F["Delete branch"]
Writing a PR people want to review
- A clear title in the same style as a commit message: "Add photo gallery page".
- What and why in a few sentences. Screenshots or the Deploy Preview link help a lot for visual changes.
- How to check it: "Open /gallery.html on the preview and resize to phone width."
- Link the issue: writing
Closes #12in the description closes issue 12 automatically when the PR is merged intomain. - Keep it small. A PR that does one thing gets a thoughtful review in minutes; a 40-file PR gets "looks good π" and hidden bugs.
Reviewing
Open the Files changed tab. Click the + beside any line to comment on it, or select several lines. When you're done, click Review changes (the label may read "Submit review") and choose Comment, Approve, or Request changes. The author answers by pushing new commits to the same branch; the PR updates itself.
For reviewers
- Does it do what the description says?
- Does the Deploy Preview look right on phone and desktop?
- Is anything missing: alt text, links, a mobile style?
- Ask questions rather than issue orders: "What do you think about�"
- Say what's good, too.
For authors
- Review your own diff before asking anyone else.
- Reply to every comment, even if just "Done".
- Remember comments are about the code, not about you.
- Disagreeing is fine; explain your reasoning.
- Thank your reviewer.
The three merge buttons
| Option | What lands on main | Good for |
|---|---|---|
| Create a merge commit | All the branch's commits plus a merge commit (what you did in Lesson 6) | Keeping the full story |
| Squash and merge | One single commit containing the whole PR | Branches with many "wip"/"fix typo" commits |
| Rebase and merge | The branch's commits replayed onto main, no merge commit | Teams who want a straight-line history |
Teams usually pick one and stick to it. After a squash merge, delete your local branch with git branch -D, because Git can't tell it was merged (the commits on main are different ones).
π Staying in Sync
Most team friction comes from working on an out-of-date copy. Three habits prevent nearly all of it:
- Pull before you branch. Always
git switch main+git pullfirst. - Keep branches short-lived. A branch merged within a day or two rarely conflicts; one left open for a month almost always does.
- Bring main into a long-running branch when main has moved on, so you deal with conflicts early and on your own terms:
git switch main
git pull
git switch add-gallery
git merge main
# resolve any conflicts, commit, then:
git push
GitHub offers the same thing as an Update branch button on the pull request when main has moved ahead.
β οΈ If git pull says "Need to specify how to reconcile divergent branches"
Your Git hasn't been told whether to merge or rebase when both sides have new commits. Run git config --global pull.rebase false once to always merge (what this class uses), then pull again. Git for Windows usually sets this for you during install.
π Conventions
Commit messages
Everything from class still applies: imperative mood, say what and why, one change per commit. Some teams add a structured prefix called Conventional Commits so tools can build changelogs automatically:
<type>(<optional scope>): <short summary>
<optional body: what changed and why>
<optional footer, e.g. Closes #12>
feat(gallery): add lightbox to photo grid
fix(nav): correct link to contact page
docs: explain local setup in README
style(css): reformat colors section
chore: remove unused images
Common types are feat (a new feature), fix (a bug fix), docs, style (formatting only), refactor, test, and chore. Use this only if your team agrees to. A plain, clear message is always fine.
Branch names
| Kind | Pattern | Example |
|---|---|---|
| New feature | feature/<short-description> | feature/photo-gallery |
| Bug fix | fix/<short-description> | fix/broken-contact-link |
| With an issue number | fix/<issue>-<description> | fix/12-mobile-menu |
| Docs / content | docs/<short-description> | docs/readme-setup |
Lowercase, hyphens instead of spaces, and short enough to type. The prefix is optional; consistency is what matters.
Shared shortcuts (aliases)
git config --global alias.st "status"
git config --global alias.lg "log --oneline --graph --all"
git config --global alias.last "log -1 --stat"
Now git lg draws the branch graph and git last summarizes your latest commit. Aliases live in your own config, so teammates each set up their own.
π€ Automated Checks (GitHub Actions)
Continuous integration (CI) means a robot runs checks on every push and pull request, so problems show up before they reach main. Your Netlify Deploy Preview is already a simple version: if the build fails, the PR shows a red β. GitHub Actions lets you add your own checks with a small YAML file in the repository.
runs your workflow"] B --> C{"Checks pass?"} C -->|"Yes β"| D["PR can be merged"] C -->|"No β"| E["Fix, commit, push again"] E --> B
For example, to check every HTML page for mistakes (unclosed tags, missing alt text, duplicate IDs), create .github/workflows/check-html.yml in VS Code:
name: Check HTML
on:
pull_request:
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
- run: npx --yes html-validate "**/*.html"
and a .htmlvalidate.json in the repository root that picks the rule set:
{
"extends": ["html-validate:recommended"]
}
Commit both files and open a pull request. A Checks section appears on the PR, and the Actions tab shows every run with its log. The recommended rules are strict; if they flag things you're happy with, the html-validate documentation explains how to switch individual rules off.
π‘ Version numbers in uses:
The @v4 after each action is its major version. Actions get new major versions every year or two; the action's page on GitHub Marketplace always shows the current one, and GitHub warns you in the run log when a version is getting old.
πΊοΈ Other Branching Models
You'll read about other workflows online. They're answers to different problems, not "more advanced" versions of GitHub Flow.
Git Flow
Designed for software shipped in numbered releases (desktop apps, libraries). Work happens on develop; release/* branches stabilize a version; main only receives finished releases (tagged v1.1, v1.2β¦); urgent hotfix/* branches start from main and merge back into both.
Trunk-based development
Everyone merges tiny changes into main (the "trunk") at least daily, relying heavily on automated tests and "feature flags" to hide unfinished work. Common at large tech companies.
| GitHub Flow | Git Flow | Trunk-based | |
|---|---|---|---|
| Long-lived branches | main | main + develop | main |
| Releases | Every merge deploys | Scheduled, versioned | Continuous |
| Branch lifetime | Hours to days | Days to weeks | Hours |
| Best fit | Websites, small teams | Versioned software | Large teams with strong CI |
| Learning curve | Gentle | Steep | Moderate (needs good tests) |
For a website deployed by Netlify, GitHub Flow is almost always the right choice.
π Secrets & Security
Keep secrets out of Git
Anything pushed to a public repository can be copied within minutes, and even deleting it later doesn't remove it from history or from other people's clones. API keys, passwords, and tokens belong in environment variables (Netlify has Site configuration β Environment variables) and in files listed in .gitignore:
# .gitignore
.env
.env.*
*.pem
*.key
GitHub's push protection blocks pushes that contain many well-known kinds of secret, and it's on by default for public repositories. Treat it as a seatbelt, not a plan.
π¨ If you do push a secret
Revoke or rotate it first: generate a new key and disable the old one on the service that issued it. Only then worry about cleaning the history. Rewriting history can't un-leak something that's already been seen.
Accounts
- Turn on two-factor authentication (GitHub requires it for most people who contribute code).
- Use HTTPS with Git Credential Manager or
gh auth login, as in Lesson 5. If you ever create a personal access token, give it the smallest permissions and an expiry date. - Review Settings β Collaborators now and then and remove people who no longer need access.
Signed commits (optional)
Anyone can put any name in user.name. Signing proves a commit really came from you, and GitHub shows a Verified badge. (Commits you make in GitHub's web editor are signed automatically.) The simplest method uses an SSH key:
ssh-keygen -t ed25519 -C "you@example.com"
git config --global gpg.format ssh
git config --global user.signingkey /path/to/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
Replace the path with where ssh-keygen saved your key (it tells you). Then on GitHub go to Settings β SSH and GPG keys β New SSH key, choose Key type: Signing Key, and paste the contents of the .pub file. GitHub's "Signing commits" documentation covers GPG keys as well.
π©Ί Team Troubleshooting
| Situation | What to do |
|---|---|
Push rejected: (fetch first) or (non-fast-forward) |
Someone else pushed first. git pull, resolve any conflict, git push. Don't force push. |
| Conflict in an image or other binary file | Git can't merge pictures line by line; you pick one. Keep yours with git restore --ours <file> or theirs with git restore --theirs <file>, then git add <file> and git commit. (Older guides write git checkout --ours, which does the same thing.) |
| "You are in 'detached HEAD' state" | You checked out a commit rather than a branch. git switch main to go back, or git switch -c <new-branch> first if you made commits you want to keep. |
I committed to main but meant to use a branch (not pushed yet) |
git switch -c my-branch takes the commit with you onto a new branch, and you can push that for a PR. Tidying main afterwards involves git reset; see Advanced Git before trying it. |
| My PR says "This branch has conflicts that must be resolved" | Bring main into your branch locally (see Staying in Sync), resolve, push. Or use GitHub's Resolve conflicts button for simple text conflicts. |
π± Where you are now
You already know the core of team Git: branch, commit, push, open a PR, review, merge, pull. The rest is agreement and habit, and every team writes its own version. When you join one, ask how they work, read their CONTRIBUTING.md if there is one, and you'll recognize almost everything. When you're ready for more power tools, head to Advanced Git.