Embercode
✕
Git and GitHubLesson 6 of 16

50 min read

Module assignment: Recipe Book repository

The project

You will build a repository called recipe-book that holds a few recipes as Markdown files. The recipes are not the point. The point is that the repository's history is the thing being graded: a reader who runs git log should see a clear, honest story, and a reader who opens your GitHub page should see a working pull request.

You will use every skill in this module: configure Git, make small commits with good messages, ignore private files, branch, merge, resolve a conflict you cause on purpose, push to GitHub, and open a pull request.

Plan on about two hours. Work in a fresh folder, and type the commands yourself. Do not copy the reference solution until you have tried.

The spec

Your finished project must have this shape.

Files in the final repository (on main):

FileContent
README.mdTitle, a one-sentence description, and a bullet list linking each recipe
chai.mdA recipe with a title and an ingredient list
poha.mdA second recipe
.gitignoreIgnores .env, *.log and a folder named notes-private/

Files that must exist on your disk but must NOT appear in git ls-files:

  • .env containing the line API_KEY=not-a-real-key
  • debug.log with any text
  • notes-private/ideas.txt with any text

History requirements:

  • At least 6 commits on main besides merge commits.
  • Every message in the imperative, under 50 characters, and meaningful (no "update", "stuff", "fix").
  • The first commit contains the .gitignore, so no private file is ever tracked.
  • One merge that is a real conflict on README.md, resolved by you, with both ideas kept.
  • One feature branch merged with --no-ff, so the graph shows the fork.
  • A tag called v1.0 on the final commit of main.

GitHub requirements:

  • A public repository named recipe-book with your work pushed.
  • One pull request, created from a branch called add-ginger-note, with a title and a description in the format "What, Why, How to check".
  • The pull request is merged, and its branch is deleted on GitHub and locally.

Example of what a correct result looks like

git log --oneline --graph --decorate on your final main should look like this (your IDs will differ):

*   829d30e (HEAD -> main, tag: v1.0, origin/main) Merge add-ginger-note
|\  
| * acd0e06 Add ginger tip to chai
|/  
*   e8e7971 Merge add-index
|\  
| * 6184af2 List recipes in README
* | ea3e8aa Describe the book in README
|/  
* 05cd3ee Add poha recipe
* a9b4968 Add masala chai recipe
* 60a34ae Add README and .gitignore

and git ls-files prints exactly:

.gitignore
README.md
chai.md
poha.md

while ls -a still shows .env, debug.log and notes-private. Finally git status -sb should say ## main...origin/main with nothing after it.

The graph shows two diamonds: one from the deliberate conflict, one from the --no-ff merge. If your graph is a straight line, you missed one of the history requirements.

Requirements checklist

Copy this list into your notes and tick items as you go.

  • Git is installed and git config --global user.name and user.email are set
  • Default branch is main (git config --global init.defaultBranch main)
  • recipe-book folder is a Git repository
  • First commit contains .gitignore and README.md
  • .env, debug.log and notes-private/ideas.txt exist but are untracked and not shown by git status
  • chai.md and poha.md each added in their own commit
  • Branch add-index changes README.md to add a recipe list
  • main changes the same line of README.md before the merge, creating a conflict
  • Conflict resolved by hand with all markers removed and both ideas kept
  • Branch add-ginger-note merged with git merge --no-ff
  • Remote origin added and main pushed with -u
  • Pull request opened, merged and the branch deleted on GitHub and locally
  • Tag v1.0 created and pushed
  • git status says the working tree is clean at the end
  • Every commit message is imperative and under 50 characters

Grading rubric

Total: 100 points. Use these descriptions to grade yourself honestly, or ask a friend to grade you.

A 60 percent solution (passes):

  • The repository exists on GitHub with the four files and a history of at least six commits.
  • .env and debug.log are not tracked.
  • A merge was done, but the conflict was avoided (for example, different lines were edited), or the PR was created but not merged.
  • Some messages are weak, for example "update README".

An 80 percent solution (good):

  • Everything in the 60 percent list, plus the conflict is real and resolved correctly, with both ideas kept and no markers left in the file.
  • Messages are imperative and clear.
  • The pull request exists, was merged, and has a description.
  • The .gitignore was committed first, so no private file ever entered history. You can prove it with git log --all --oneline -- .env, which prints nothing.
  • Missing: the tag, or the --no-ff merge, or the clean-up of branches.

A 100 percent solution (job-ready habits):

  • All checklist items pass, and the log graph matches the example shape.
  • Commit messages follow the rules from lesson 3 (imperative, under 50 characters, one purpose each).
  • The PR description follows the What, Why, How-to-check format.
  • Branches are deleted locally and on GitHub; git branch -a shows only main.
  • You wrote a short README.md section called "How this repo was built" listing the commands you used for the conflict, in your own words.
  • You can explain every command you used, which you will test by answering the questions at the end.

Step-by-step hints

Open each hint only when you are stuck. Each one gives direction without writing the whole answer.

Hint 1: Setup and the first commit

Create the folder, run git init, and check git status. Create the private files first if you like, and the .gitignore before you run any git add. Write the three patterns, one per line: .env, *.log, notes-private/. Stage .gitignore and README.md by name (not with git add .), commit, then run git status -s and confirm the private files are not listed.

Hint 2: Recipes

Make chai.md and commit it alone. Make poha.md and commit it alone. Two commits, two stories. A printf with \n is the quickest way to write several lines from the terminal, or use your editor.

Hint 3: Connect to GitHub

Create an empty public repository on GitHub with no README. Copy its URL. Run git remote add origin <url>, then git push -u origin main. Check the files appear on the web page.

Hint 4: Causing the conflict

You need the same line changed on two branches. Create add-index with git switch -c add-index, and change the description line of README.md while adding the recipe list. Commit. Switch back to main and change only the description line to something different. Commit. Run git merge add-index and read CONFLICT. Run git status and open the file.

Hint 5: Resolving it

Between <<<<<<< HEAD and ======= is the version on main. Between ======= and >>>>>>> add-index is the version from the branch. Keep the description from main and the bullet list from the branch, and delete all three marker lines. Then git add README.md and git commit -m "Merge add-index". If you panic, git merge --abort returns to the state before the merge and you can try again.

Hint 6: The no-fast-forward merge

Create add-ginger-note from main, add a line to chai.md, and commit. Push it with git push -u origin add-ginger-note and open a pull request on GitHub for practice. For the local history requirement, you can merge on the command line instead: git switch main, then git merge --no-ff add-ginger-note. If you merge on GitHub, press the Merge pull request button (which makes a merge commit) and run git pull locally. Either way the graph shows a fork and join.

Hint 7: Clean-up and tag

Delete the branch locally with git branch -d add-ginger-note and on the remote with git push origin --delete add-ginger-note. Create the tag with git tag v1.0 and push it with git push origin v1.0. Check that git branch -a lists only main and the remote-tracking origin/main.

Hint 8: Prove the history is clean

Run git log --oneline --graph --decorate, git ls-files, git status -sb and git log --all --oneline -- .env. Compare with the example. The last command must print nothing.

Reference solution

Try the project first. Then compare with this complete command sequence. It was run on Git 2.53 and produced the output shown earlier.

Reference solution: all commands

Set up once (skip if already done):

git config --global user.name "Asha Rao"
git config --global user.email "asha@example.com"
git config --global init.defaultBranch main

Create the project and the private files:

mkdir recipe-book
cd recipe-book
git init
echo "API_KEY=not-a-real-key" > .env
echo "something went wrong" > debug.log
mkdir notes-private
echo "try a mango lassi" > notes-private/ideas.txt

First commit: ignore rules and README, nothing private.

printf '.env\n*.log\nnotes-private/\n' > .gitignore
printf '# Recipe Book\n\nMy favourite recipes.\n' > README.md
git add .gitignore README.md
git commit -m "Add README and .gitignore"
git status -s

git status -s prints nothing, because the three private items are ignored.

Recipes, one commit each:

printf '# Masala Chai\n\n- 2 cups water\n- 1 cup milk\n- 2 tsp tea leaves\n' > chai.md
git add chai.md
git commit -m "Add masala chai recipe"
 
printf '# Poha\n\n- 1 cup flattened rice\n- 1 onion\n' > poha.md
git add poha.md
git commit -m "Add poha recipe"

Connect to GitHub (replace the URL with yours) and push:

git remote add origin https://github.com/yourname/recipe-book.git
git push -u origin main

The conflict, on purpose. Branch first:

git switch -c add-index
printf '# Recipe Book\n\nMy favourite recipes.\n\n- [Chai](chai.md)\n- [Poha](poha.md)\n' > README.md
git commit -am "List recipes in README"

Back on main, change the same description line differently:

git switch main
printf '# Recipe Book\n\nMy favourite recipes, tested at home.\n' > README.md
git commit -am "Describe the book in README"

Merge and meet the conflict:

git merge add-index
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

git status -s shows UU README.md. The file now reads:

# Recipe Book
 
<<<<<<< HEAD
My favourite recipes, tested at home.
=======
My favourite recipes.
 
- [Chai](chai.md)
- [Poha](poha.md)
>>>>>>> add-index

Resolve by keeping the new description and the list:

printf '# Recipe Book\n\nMy favourite recipes, tested at home.\n\n- [Chai](chai.md)\n- [Poha](poha.md)\n' > README.md
git add README.md
git commit -m "Merge add-index"
git push

The feature branch, pull request and no-fast-forward merge:

git switch -c add-ginger-note
printf '\nTip: crush the ginger before boiling.\n' >> chai.md
git commit -am "Add ginger tip to chai"
git push -u origin add-ginger-note

Open a pull request on GitHub from add-ginger-note into main. Use a description like:

What: Adds a ginger tip to the chai recipe.
Why: Crushed ginger gives a stronger flavour.
How to check: Open chai.md and read the last line.

Merge the PR on GitHub (or locally with git merge --no-ff add-ginger-note -m "Merge add-ginger-note" followed by git push). Then update, clean up and tag:

git switch main
git pull
git branch -d add-ginger-note
git push origin --delete add-ginger-note
git tag v1.0
git push origin v1.0

Check the result:

git log --oneline --graph --decorate
git ls-files
git status -sb
git log --all --oneline -- .env

The last command prints nothing, which proves the key was never committed. If you merged on GitHub rather than locally, git pull creates the same shape of graph, and the branch deletion may already be done by GitHub: skip the delete command if you see an error that the remote ref does not exist.

Extension challenges

Finished early? Try these, in order of difficulty.

  1. Revert a mistake. Add a bad commit Add broken link, push it, and then undo it safely with git revert. Explain in the PR why you did not use --amend.
  2. Simulate a teammate. Clone the repository into a second folder, make a commit there and push it. In the first folder, make a different commit, try to push, read the rejection, and fix it with git pull --no-rebase.
  3. Add a CONTRIBUTING file. In a new branch, write CONTRIBUTING.md with three rules for good commit messages, and open a PR.
  4. Find the author. Use git blame README.md to find which commit introduced the bullet list.

Questions to answer in your own words

Answer these in a text file or aloud. If you cannot, reread the matching lesson.

  1. Why did you commit .gitignore before the private files could be staged?
  2. In the conflict, which side was HEAD, and why?
  3. What does git push -u remember, and why do you not need -u the second time?
  4. What would have happened if you had run git push --force after a rejected push in the extension challenge?
  5. Why does the git log --all --oneline -- .env command print nothing, and what would it print if you had committed .env and deleted it later?
Answers to the questions
  1. Ignore rules apply only to untracked files. Once a file is committed, adding it to .gitignore does not remove it, and the old copy stays in history. Committing the rules first means the private files were never tracked.
  2. HEAD was main, the branch you stood on when you ran git merge add-index. The text after ======= came from the branch you merged in.
  3. -u records that local main tracks origin/main, so later plain git push and git pull know where to go. It is stored in the repository's config, so it is needed only once per branch.
  4. Force would overwrite the remote main with your copy and delete the teammate's commit from GitHub. The right fix is git pull then git push.
  5. The file was never tracked, so no commit touches it. If it had been committed and deleted later, the command would list both the commit that added it and the commit that removed it, proving the secret remains in history.

Summary of the module

  • Version control records history so you can undo, trace and collaborate; Git is the local tool, GitHub the hosting service.
  • Install Git, set your name and email, and set main as the default branch.
  • Move changes working directory, staging area, repository; write imperative messages; ignore secrets with .gitignore.
  • Branches are pointers; merges are fast-forward or three-way; conflicts are resolved by editing markers, git add, git commit.
  • Push and pull connect your repo to a remote; a rejected push means pull first; pull requests and forks are how teams and open source collaborate.

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.