Project: A Team Workflow from Start to Finish

This final project puts the whole course together. Two people, Sam and Alex, build a small recipe website together: they create a repository, share it on GitHub, work on feature branches in parallel, review each other's work, resolve a conflict, and tag a release. Every command was run for real — follow along with two folders to play both roles.

Step 1 — Sam creates the project

$
mkdir recipe-site
cd recipe-site
git init --quiet
printf '<h1>Family Recipes</h1>\n<ul>\n  <li>Pancakes</li>\n</ul>\n' > index.html
printf 'node_modules/\n.env\n*.log\n' > .gitignore
printf '# Recipe Site\n\nOur family recipes, online.\n' > README.md
git add .
git commit -m "Create recipe site with home page"
Output
[main (root-commit) ad384c4] Create recipe site with home page
 3 files changed, 10 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 README.md
 create mode 100644 index.html

Step 2 — Publish it

Sam creates an empty recipe-site repository on GitHub (no README), then connects and pushes:

$
git remote add origin https://github.com/sam-rivera/recipe-site.git
git push -u origin main
Output
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 10 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 425 bytes | 425.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipe-site.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

Step 3 — Alex joins

Sam adds Alex as a collaborator on GitHub. Alex clones the repository and sets their identity for it:

$
cd ~
git clone https://github.com/sam-rivera/recipe-site.git alex-site
cd alex-site
git config user.name "Alex Chen"
git config user.email "alex@example.com"
git log --oneline
Output
Cloning into 'alex-site'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (5/5), done.
ad384c4 (HEAD -> main, origin/main, origin/HEAD) Create recipe site with home page

Step 4 — Two features in parallel

Alex works on a desserts section:

$
git switch -c feature/desserts
printf '<h1>Family Recipes</h1>\n<ul>\n  <li>Pancakes</li>\n  <li>Brownies</li>\n</ul>\n' > index.html
git commit -qam "Add brownies to the recipe list"
printf '<h1>Brownies</h1>\n<p>Chocolate, butter, sugar, eggs, flour.</p>\n' > brownies.html
git add brownies.html
git commit -qm "Add brownie recipe page"
git push -u origin feature/desserts
Output
Switched to a new branch 'feature/desserts'
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 10 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 659 bytes | 659.00 KiB/s, done.
Total 6 (delta 1), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipe-site.git
 * [new branch]      feature/desserts -> feature/desserts
branch 'feature/desserts' set up to track 'origin/feature/desserts'.

At the same time, Sam works on breakfast recipes in the other folder:

$
cd ~/recipe-site
git switch -c feature/breakfast
printf '<h1>Family Recipes</h1>\n<ul>\n  <li>Pancakes</li>\n  <li>Shakshuka</li>\n</ul>\n' > index.html
git commit -qam "Add shakshuka to the recipe list"
git push -u origin feature/breakfast
Output
Switched to a new branch 'feature/breakfast'
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 10 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 324 bytes | 324.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipe-site.git
 * [new branch]      feature/breakfast -> feature/breakfast
branch 'feature/breakfast' set up to track 'origin/feature/breakfast'.

Step 5 — Review and merge the first pull request

Both open pull requests. Sam reviews Alex's desserts PR, approves it, and merges it on GitHub with a merge commit:

$
git fetch --prune
git log --oneline --graph --all
Output
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), 849 bytes | 424.00 KiB/s, done.
From https://github.com/sam-rivera/recipe-site
   ad384c4..e397a0b  main       -> origin/main
*   e397a0b (origin/main) Merge pull request #1 from sam-rivera/feature/desserts
|\
| * d5b34bf Add brownie recipe page
| * b5f4efc Add brownies to the recipe list
|/
| * a771dcf (HEAD -> feature/breakfast, origin/feature/breakfast) Add shakshuka to the recipe list
|/
* ad384c4 (main) Create recipe site with home page

Step 6 — Update the second branch and resolve a conflict

GitHub reports that Sam's breakfast PR now has conflicts: both branches added a line at the same spot in index.html. Sam rebases the branch onto the new main:

$
git rebase origin/main
Output
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: could not apply a771dcf... Add shakshuka to the recipe list
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply a771dcf... Add shakshuka to the recipe list
$
cat index.html
Output
<h1>Family Recipes</h1>
<ul>
  <li>Pancakes</li>
<<<<<<< HEAD
  <li>Brownies</li>
=======
  <li>Shakshuka</li>
>>>>>>> a771dcf (Add shakshuka to the recipe list)
</ul>

Both recipes should stay. Sam writes the combined list, stages it and continues:

$
printf '<h1>Family Recipes</h1>\n<ul>\n  <li>Pancakes</li>\n  <li>Shakshuka</li>\n  <li>Brownies</li>\n</ul>\n' > index.html
git add index.html
GIT_EDITOR=true git rebase --continue
git push --force-with-lease
Output
[detached HEAD 6ba611e] Add shakshuka to the recipe list
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/feature/breakfast.
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 10 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 338 bytes | 338.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipe-site.git
 + a771dcf...6ba611e feature/breakfast -> feature/breakfast (forced update)

The force push is safe here: feature/breakfast is Sam's own branch, and --force-with-lease would refuse if anyone else had pushed to it.

Step 7 — Merge with squash

Alex reviews and merges the breakfast PR, this time with Squash and merge:

$
git switch main
git pull --prune
Output
Switched to branch 'main'
Your branch is behind 'origin/main' by 3 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
From https://github.com/sam-rivera/recipe-site
 - [deleted]         (none)     -> origin/feature/breakfast
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 319 bytes | 319.00 KiB/s, done.
   e397a0b..409dfb2  main       -> origin/main
Updating ad384c4..409dfb2
Fast-forward
 brownies.html | 2 ++
 index.html    | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 brownies.html
$
git branch -d feature/breakfast
Output
error: The branch 'feature/breakfast' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/breakfast'.

As explained in lesson 36, a squash merge creates a new commit, so Git can't tell the branch was merged. Sam checks the PR on GitHub shows "Merged", then deletes the branch with -D:

$
git branch -D feature/breakfast
git log --oneline --graph
Output
Deleted branch feature/breakfast (was 6ba611e).
* 409dfb2 (HEAD -> main, origin/main) Add shakshuka to the recipe list (#2)
*   e397a0b Merge pull request #1 from sam-rivera/feature/desserts
|\
| * d5b34bf Add brownie recipe page
| * b5f4efc Add brownies to the recipe list
|/
* ad384c4 Create recipe site with home page

Step 8 — Tag a release

$
git tag -a v1.0.0 -m "First release: pancakes, shakshuka and brownies"
git push origin v1.0.0
Output
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 188 bytes | 188.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/recipe-site.git
 * [new tag]         v1.0.0 -> v1.0.0

Step 9 — Alex catches up

Alex switches back to main, pulls everything — including the tag — and cleans up:

$
cd ~/alex-site
git switch main
git pull --prune
Output
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
From https://github.com/sam-rivera/recipe-site
 - [deleted]         (none)     -> origin/feature/desserts
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), 685 bytes | 342.00 KiB/s, done.
   ad384c4..409dfb2  main       -> origin/main
 * [new tag]         v1.0.0     -> v1.0.0
Updating ad384c4..409dfb2
Fast-forward
 brownies.html | 2 ++
 index.html    | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 brownies.html
$
git branch -d feature/desserts
git describe
Output
Deleted branch feature/desserts (was d5b34bf).
v1.0.0

What you practised

StepLessons
Create a repository, ignore files, commit05, 08–10, 14
Publish to GitHub, clone30–33
Feature branches and pushing with upstreams22–23, 32, 35
Pull requests, merge commits and squash merges24, 36
Rebasing and resolving a conflict25–26
Force-pushing safely, pruning, deleting branches32, 35
Tagging a release39

Where to go next

  • Use Git for everything you write — code, notes, configuration — until the commands become habits.
  • Contribute a small fix to an open-source project using the fork workflow (lesson 37).
  • Add a CI workflow to one of your repositories (lesson 49).
  • Test yourself with the exercises and the quiz, and keep the cheat sheet handy.