Advanced

How teams actually stay in sync

Behind every well-ordered GitHub project there's a workflow everyone agreed to follow, and the most common one is simple: one branch per task, every branch reviewed through a pull request, and main kept clean all the time. The point isn't the ceremony, it's that anyone can start work without tripping over someone else's.

What you will see

Merging squash merge of #1 into main The branch was deleted and synced.

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

I kept merging main into my feature branch at the wrong moment, and the conflict I got later was just friction I had not synced out. Let me run a small team flow myself: one branch per task, keep it current, and open a PR that merges cleanly because I stayed in sync the whole time.

1

Start a real main with a few commits, then create a task branch. Giving the task its own branch keeps it out of main and makes it easy to trace who did what. The branch name should say what is inside it.

echo "landing page" > index.html git add index.html git commit -m "Add landing page" git switch -c feature/search

Before you run it, say out loud what it will print. Then open the answer.

What it prints
[main abc1234] Add landing page 1 file changed, 1 insertion(+) Switched to a new branch "feature/search"
Create the branch before doing the work, so every step lands on feature/search. Doing it after the first commit on main is how a feature accidentally lands on main.
2

Do the actual task work on the branch. While I am away, a teammate keeps merging into main, so main will drift ahead of my branch. That is exactly what makes staying current matter later.

echo "search box" >> index.html git add index.html git commit -m "Add search box"

Before you run it, say out loud what it will print. Then open the answer.

What it prints
[feature/search def4567] Add search box 1 file changed, 1 insertion(+)
The branch name in brackets confirms the commit went to the feature branch. main still has only the landing page commit.
3

While I was working, main moved ahead. Pull their latest into main first, then bring that into my branch and merge, so my work stays in sync. This is the habit that stops small conflicts from building up into a fight later.

git switch main git pull git switch feature/search git merge main

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Switched to branch 'feature/search' Updating abc..def Fast-forward index.html | 1 insertion(+)
Merge main into feature before opening a PR, not after. By the time the PR opens, most of the friction is already gone, and it usually fast-forwards instead of fighting.
4

Push the branch and open the pull request. The branch has the work and main is clean, so the diff the reviewer sees is exactly the search box. I add a short note so anyone reading knows what changed and why.

git push -u origin feature/search

Before you run it, say out loud what it will print. Then open the answer.

What it prints
To https://github.com/ada/myapp.git * [new branch] feature/search -> feature/search Branch feature/search set up to track remote branch feature/search from origin.
The -u flag records that this branch tracks origin/feature/search, so future git push and git pull need no arguments. After the first time, a plain git push is enough.
5

Once a reviewer approves, merge the pull request. The branch joins main and the request closes, so the history now shows exactly what shipped. Because I stayed in sync the whole time, main stays clean and still works.

gh pr merge 1 --squash

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Merging squash merge of #1 into main The branch was deleted and synced.
Use squash so the merge is one clean commit on main, not a pile. After it runs, the branch is done and can be removed.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

Each task runs on its own branch, gets reviewed, then merges into main.
1 Create a task branch
↓
2 Do the work there
↓
3 Open a PR
↓
4 Review and merge to main
Why this works
Branch per task

Give each task its own branch. Work stays separated and every change goes through review, which makes it easy to trace who did what and why.

Code review rules

Require at least one approval before merging. It catches mistakes early and spreads knowledge around the team so no single person holds all the context.

Examples

A team flow that scales

Dev A:  branch feature/login
Dev B:  branch feature/search
Both:   open PRs
Reviewers: approve and merge
main:   stays clean and always works

Everyone works on their own branch so the code never blocks on another person. PRs keep reviews fast, and main is always close to shippable. It's an unglamorous setup, which is exactly what makes it reliable across a whole team.

Branch names that say what they are

feature/login
fix/search-bug
docs/api-guide

A clear branch name tells you what's inside before you open anything. Start with a type, feature/, fix/, docs/: then add a short slug. A new teammate can usually guess the contents from the name alone.

Keeping your branch current

git switch main
git pull
git switch feature/login
git merge main

While you're away, teammates keep merging into main. Pull their changes into your branch now and then, so your work stays in sync. It stops surprise conflicts from building up, by the time you open your PR, most of the friction is already gone.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← What a conflict actually is (and why it isn't a disaster)
Previous Lesson
What a conflict actually is (and why it isn't a disaster)
Next Lesson
Setting up your first automated workflow
Setting up your first automated workflow →