Post

Git Part 1 — Practical Exam (Week 1)

Git Part 1 — Practical Exam (Week 1)

Git Part 1 — Practical Exam (Week 1)

Format:Execute each step in your terminal. Write the exact command you used and paste the output in the space provided.
Duration:~3 hours
Covers:Setup, local workflow, .gitignore, mistake recovery, remote collaboration, vi editor, branching, merging, merge conflicts


Question 1 — Setting Up a Repository

Scenario:You are starting a new project from scratch and need to set it up with Git.

Tasks — execute in order:

  1. Create a new directory called my_project and navigate into it.
  2. Create an empty file called notes.txt.
  3. Verify the file exists using ls.
  4. Initialize a Git repository in this directory.
  5. Run git status and write down what Git says about notes.txt.

Expected output clue:Git should show notes.txt as an untracked file.

Questions to answer:

  • What command initializes a Git repository?
  • What does “untracked” mean in Git?
  • Where is the Git history physically stored after you run git init?

Answer — Question 1

Commands:

1
2
3
4
5
6
mkdir my_project
cd my_project
touch notes.txt
ls
git init
git status

Expected output of git status:

1
2
3
4
5
6
7
8
9
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        notes.txt

nothing added to commit but untracked files present (use "git add" to track)

git init— initializes a Git repository. It creates a hidden .git folder in the current directory that stores all version history, configuration, and Git metadata.

“Untracked”means Git can see the file exists on disk but is not watching it. Git will not include this file in any commit until you explicitly run git add on it.

Where history is stored:In the hidden .git folder inside your project directory. Running ls -a will show it. Do not delete this folder — it contains your entire project history.


Question 2 — Staging and Committing

Scenario:You want to save your notes.txt file into the Git history.

Tasks — execute in order:

  1. Stage notes.txt using the appropriate git add command.
  2. Run git status — what has changed compared to Question 1?
  3. Commit the staged file with the message "initial commit".
  4. Run git status again — what does Git say now?
  5. Run git log --oneline and note the short commit hash.

Questions to answer:

  • What is the difference between staging a file and committing it?
  • What does “working tree clean” mean?
  • Draw or describe Git’s three-area model (working directory, staging area, repository).

Answer — Question 2

Commands:

1
2
3
4
5
git add notes.txt
git status
git commit -m "initial commit"
git status
git log --oneline

git status after git add:

1
2
3
4
5
6
7
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   notes.txt

git status after git commit:

1
2
On branch main
nothing to commit, working tree clean

Staging vs. Committing:

  • Staging(git add) moves the file into the staging area (the “index”). You are telling Git “include this in the next snapshot.” Nothing is permanently saved yet.
  • Committing(git commit) takes everything currently staged and saves it as a permanent, named snapshot in the repository with a unique hash and your message.

“Working tree clean”means every tracked file on disk exactly matches the last commit. No unstaged edits, no staged-but-uncommitted changes, nothing waiting.

Three-area model:

1
2
3
Working Directory  git add  Staging Area  git commit  Repository
(files you edit)                 (changes ready                  (permanent
                                  to commit)                      snapshots)

Question 3 — Modifying Files and Viewing Differences

Scenario:You add content to notes.txt and want to understand exactly what changed before committing.

Tasks — execute in order:

  1. Add the text "first line" to notes.txt using echo (overwrite, not append).
  2. Run git status — what does Git say about notes.txt now?
  3. Run git diff notes.txt — what does the output show?
  4. Stage notes.txt with git add.
  5. Run git diff notes.txt again — what happens? Why?
  6. Run git diff --staged notes.txt — what does this show?
  7. Commit with message "added first line".
  8. Append "second line" to notes.txt (use >>).
  9. Stage and commit with message "added second line".
  10. Run git log --oneline --graph --decorate — how many commits do you see?

Questions to answer:

  • What is the difference between git diff and git diff --staged?
  • What is the difference between > and >> with echo?
  • What do the + and - symbols mean in git diff output?

Answer — Question 3

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
echo "first line" > notes.txt
git status
git diff notes.txt
git add notes.txt
git diff notes.txt
git diff --staged notes.txt
git commit -m "added first line"

echo "second line" >> notes.txt
git add notes.txt
git commit -m "added second line"

git log --oneline --graph --decorate

git diff output (after editing, before staging):

1
2
3
4
5
6
diff --git a/notes.txt b/notes.txt
index e69de29..8b13789 100644
--- a/notes.txt
+++ b/notes.txt
@@ -0,0 +1 @@
+first line

git diff after staging:produces no output. The working directory now matches the staging area — there is nothing unstaged to show.

git diff --staged:shows what IS staged — the change that is about to be committed.

git log --oneline --graph --decorate:

1
2
3
* a3b2c1d (HEAD -> main) added second line
* 9f8e7d6 added first line
* a1b2c3d initial commit

git diff vs git diff --staged: | Command | Compares | Shows | |———|———-|——-| | git diff | Working directory vs staging area | Unstaged changes | | git diff --staged | Staging area vs last commit | What WILL be committed |

> vs >>:> overwrites the file completely. >> appends to the end of the file.

+ and - in diff output:Lines starting with + are additions (new content). Lines starting with - are deletions (removed content).


Question 4 — Using .gitignore

Scenario:Your project generates temporary files that should never be committed.

Tasks — execute in order:

  1. Create a file called debug.log.
  2. Run git status — is debug.log visible to Git?
  3. Create a .gitignore file with the pattern *.log inside it.
  4. Run git status again — what happened to debug.log?
  5. Create another file error.log — does it also disappear from git status?
  6. Stage and commit .gitignore with an appropriate commit message.

Questions to answer:

  • What is the purpose of .gitignore?
  • What does the * wildcard mean in a .gitignore pattern?
  • Name three types of files that should always be in .gitignore in a real project.
  • If you have already committed a file and then add it to .gitignore, will Git stop tracking it?

Answer — Question 4

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
touch debug.log
git status

echo "*.log" > .gitignore
git status

touch error.log
git status

git add .gitignore
git commit -m "Add .gitignore to exclude log files"
git status

git status before .gitignore:shows debug.log as untracked.

git status after .gitignore:debug.log disappears. After creating error.log, it also does not appear — both match the *.log pattern.

Purpose of .gitignore:Tells Git to completely ignore certain files. They will never appear as untracked, cannot be accidentally staged, and will not be committed.

* wildcard:Matches any sequence of characters. *.log matches debug.log, error.log, server.log — any file ending in .log.

Three types of files to always ignore:

  1. Secret/credential files (.env, secrets.yml, *.pem) — never commit passwords or API keys
  2. Build artifacts / compiled output (dist/, build/, *.pyc, node_modules/)
  3. Editor/OS junk (.DS_Store, .idea/, *.swp)

Already-committed files:No, .gitignore does NOT stop tracking files already committed. You need to run git rm --cached <file> first to untrack it, then commit that removal.


Question 5 — Unstaging a File

Scenario:You accidentally staged a file you did not mean to commit.

Tasks — execute in order:

  1. Create two files: feature.py and accident.py.
  2. Stage bothfiles with git add.
  3. Run git status — confirm both are staged.
  4. Unstage only accident.py using the modern command git restore --staged.
  5. Run git status — what is the state of each file now?
  6. Commit only feature.py with message "add feature file".
  7. Confirm accident.py is still untracked after the commit.

Questions to answer:

  • What is the modern command to unstage a file? What is the older equivalent?
  • After unstaging accident.py, is the file deleted from disk? Explain.
  • What is the difference between git restore --staged <file> and git restore <file> (without --staged)?

Answer — Question 5

Commands:

1
2
3
4
5
6
7
8
9
touch feature.py accident.py
git add feature.py accident.py
git status

git restore --staged accident.py
git status

git commit -m "add feature file"
git status

git status after staging both:

1
2
3
Changes to be committed:
    new file:   accident.py
    new file:   feature.py

git status after git restore --staged accident.py:

1
2
3
4
5
Changes to be committed:
    new file:   feature.py

Untracked files:
    accident.py

git status after commit:

1
2
Untracked files:
    accident.py

Modern vs old command:

  • Modern (Git 2.23+): git restore --staged <file>
  • Old: git reset HEAD <file> Both do exactly the same thing.

Is accident.py deleted?No. The file exists on disk unchanged. Only its presence in the staging area was removed.

git restore --staged vs git restore:

  • git restore --staged <file> — removes the file from the staging area only. The edit remains in the working directory.
  • git restore <file>discards your working directory changesand reverts the file to the last committed version. This CANNOT be undone. Use with care.

Question 6 — Understanding Reset Modes

Scenario:You need to undo commits in different situations — sometimes keeping your work, sometimes discarding it.

Tasks — execute in order:

  1. Stage and commit accident.py (from Q5) with message "oops committed accident".
  2. Verify the commit exists with git log --oneline.
  3. Use git reset --soft HEAD~1 to undo the last commit.
  4. Run git status — where is accident.py now?
  5. Run git log --oneline — is the commit still there?
  6. Unstage accident.py with git restore --staged accident.py.
  7. Now commit a new file another_mistake.py with message "another mistake".
  8. Use git reset --mixed HEAD~1 (or just git reset HEAD~1) to undo it.
  9. Run git status — where is another_mistake.py now?
  10. Commit a new file delete_this.py with message "delete this entirely".
  11. Use git reset --hard HEAD~1 to undo it completely.
  12. Run git status AND check if delete_this.py still exists on disk with ls.

Questions to answer:

  • Describe in one sentence what each reset mode does to the staging area and working directory.
  • When would you use --soft? When --mixed? When --hard?
  • What is the danger of --hard? Can you recover from it?

Answer — Question 6

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
git add accident.py
git commit -m "oops committed accident"
git log --oneline

git reset --soft HEAD~1
git status
git log --oneline
git restore --staged accident.py

touch another_mistake.py
git add another_mistake.py
git commit -m "another mistake"
git reset --mixed HEAD~1
git status

touch delete_this.py
git add delete_this.py
git commit -m "delete this entirely"
git reset --hard HEAD~1
git status
ls

After --soft:accident.py appears in “Changes to be committed” — still staged. Commit is gone from log.

After --mixed:another_mistake.py appears in “Changes not staged for commit” — in working directory, not staged. Commit gone from log.

After --hard:Working tree is completely clean. delete_this.py is gone — not on disk, not staged, not anywhere.

The three modes: | Mode | HEAD moves | Staging area | Working directory | |——|———–|————–|——————-| | --soft | back | unchanged (still staged) | unchanged | | --mixed (default) | back | cleared | unchanged | | --hard | back | cleared | reverted — changes lost|

When to use each:

  • --soft — “I committed too early, want to add one more change first”
  • --mixed — “I committed the wrong mix of files, want to re-sort and re-stage”
  • --hard — “This entire commit was a mistake, throw everything away”

Danger of --hard:The working directory changes are permanently deleted — Git never tracked them, so there is no history to recover from. However, if you had committed the work before, git reflog can help you find the commit hash and reset back to it.


Question 7 — vi Editor

Scenario:You need to edit a file using vi — a skill required when working on servers or when Git opens an editor for commit messages.

Tasks — execute in order:

  1. Open a new file in vi: vi practice.txt
  2. Enter insert mode and type three lines:
    1
    2
    3
    
    Line 1: Git tracks changes
    Line 2: Branches allow parallel work
    Line 3: Commits are permanent snapshots
    
  3. Return to Normal mode (press Esc).
  4. Save the file without quitting (:w then Enter).
  5. Navigate to line 1 using gg.
  6. Navigate to line 3 using G.
  7. Delete line 3 using dd.
  8. Undo the deletion using u.
  9. Save and quit with :wq.
  10. Verify the content: cat practice.txt

Now try the Git commit editor:

  1. Make a change to any file and stage it.
  2. Run git commit (WITHOUT the -m flag) — vi opens.
  3. Press i, type your commit message on the first line.
  4. Press Esc, then :wq to save.
  5. Verify the commit with git log --oneline.

Questions to answer:

  • What are the three modes in vi? What does each mode do?
  • What is the difference between :q and :q!?
  • If you are stuck in vi and just want to exit without saving, what do you type?

Answer — Question 7

vi commands used:

1
2
3
4
5
6
7
8
i          enter Insert mode
Esc        return to Normal mode
:w         save (write) without quitting
gg         go to top of file
G          go to bottom of file
dd         delete current line
u          undo
:wq        save and quit

Three modes in vi:

  • Normal mode(default when you open vi): Keypresses are commands, not text. dd deletes a line, gg jumps to top, etc. Press Esc to get here from any other mode.
  • Insert mode: Keypresses type text normally. Press i to enter. Press Esc to return to Normal.
  • Command-line mode: Appears at the bottom of the screen after pressing :. Run commands like save, quit, search. Press Enter to execute, Esc to cancel.

:q vs :q!:

  • :q — quits only if there are no unsaved changes. Git will refuse to quit if you have made edits.
  • :q! — force-quits WITHOUT saving. The ! means “I know what I am doing, just do it.”

Stuck in vi, want out without saving:

1
Esc    :q!    Enter

Press Esc first (always), then type :q! and press Enter.


Question 8 — Connecting to a Remote Repository

Scenario:You want to back up your local repository to GitHub and sync with a teammate’s change.

Tasks — execute in order:

  1. Create an empty repository on GitHub — do NOT tick “Add README”.
  2. Copy the SSH URL of the remote repository.
  3. Link your local my_project to the remote: git remote add origin <URL>
  4. Verify: git remote -v
  5. Push all local commits: git push -u origin main
  6. Run git status — what does Git say about your branch now?
  7. Using the GitHub web interface, create a new file team_notes.txt and commit it (simulating a teammate’s push).
  8. In your terminal, run git fetch.
  9. Run git diff main..origin/main — what does this show?
  10. Run git pull to sync.
  11. Run git diff main..origin/main again — what is the output now?

Questions to answer:

  • What does the -u flag in git push -u origin main do?
  • What is the difference between git fetch and git pull?
  • Why is it better to run git fetch then git diff before git pull?

Answer — Question 8

Commands:

1
2
3
4
5
6
7
8
git remote add origin [email protected]:youruser/my_project.git
git remote -v
git push -u origin main
git status
git fetch
git diff main..origin/main
git pull
git diff main..origin/main

git status after push:

1
2
3
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean

git diff main..origin/mainbefore pull: shows the new team_notes.txt content added by the teammate (lines prefixed with +).

git diff main..origin/mainafter pull: no output — local and remote are now identical.

The -u flag:Sets upstream tracking — links your local main to origin/main. After this, you can run git push and git pull without typing origin main every time.

git fetch vs git pull:

  • git fetch downloads remote commits into remote-tracking branches (like origin/main) but does NOT merge into your working files. Safe — inspect first.
  • git pull = git fetch + git merge. Downloads and immediately merges. Your files update in one step.

Why fetch then diff first:Lets you review what your teammate changed before it enters your codebase. If their changes conflict with yours, you can plan the merge rather than being surprised.


Question 9 — Cloning and Branching

Scenario:You are joining a project that already exists on GitHub. You need to clone it and create a feature branch.

Tasks — execute in order:

  1. Clone the my_project repository to a NEW folder called my_project_clone:
    1
    
    git clone <your-remote-url> my_project_clone
    
  2. Navigate into my_project_clone and run git log --oneline — what do you see?
  3. Create a new branch called feature-about and switch to it in one command.
  4. Create a file about.html and add content: "<h1>About Us</h1>".
  5. Stage and commit with message "Add about page".
  6. Run git log --oneline --graph --decorate — what does the output show about the branches?
  7. Switch back to main and check git log --oneline — is the about.html commit there?
  8. Run ls on main — is about.html visible?
  9. Merge feature-about into main.
  10. Run git log --oneline --graph — what type of merge happened (fast-forward or three-way)?
  11. Delete the feature-about branch.

Questions to answer:

  • What does git clone do? What does it set up automatically?
  • What is the difference between a fast-forward merge and a three-way merge?
  • Why should you work on a feature branch instead of directly on main?

Answer — Question 9

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
git clone <remote-url> my_project_clone
cd my_project_clone
git log --oneline

git checkout -b feature-about

echo "<h1>About Us</h1>" > about.html
git add about.html
git commit -m "Add about page"

git log --oneline --graph --decorate

git checkout main
git log --oneline
ls

git merge feature-about
git log --oneline --graph

git branch -d feature-about

git log on feature-about shows:

1
2
3
* c7d8e9f (HEAD -> feature-about) Add about page
* b3c4d5e (main, origin/main) [previous commit]
...

After switching to main:The about.html commit is NOT in git log, and about.html does NOT appear in ls. The file only exists on feature-about.

After merge — fast-forward output:

1
2
3
Updating b3c4d5e..c7d8e9f
Fast-forward
 about.html | 1 +

What git clone sets up automatically:

  • Downloads the entire repository (all history, all branches)
  • Sets origin to point back at the source URL
  • Checks out the default branch
  • Sets up upstream tracking

Fast-forward vs three-way merge:

  • Fast-forward: main has not changed since the branch was created. Git simply moves main forward to the branch tip — no extra merge commit.
  • Three-way merge: Both main and the feature branch have new commits since branching. Git creates a new merge commit that combines both histories. You see a fork-and-join shape in git log --graph.

Why use feature branches:main stays stable and deployable. Others can review your changes (via pull request) before they reach main. Multiple features can be developed in parallel without blocking each other.


Question 10 — Remote Branches

Scenario:You want to push your feature branch to GitHub so a teammate can review it.

Tasks — execute in order:

  1. In my_project_clone, create a new branch feature-contact.
  2. Create contact.html with the content "<h1>Contact Us</h1>" and commit it.
  3. Push the branch to the remote: git push -u origin feature-contact
  4. Verify on GitHub that the branch appears.
  5. Run git branch -a — what do you see?
  6. Now delete the remote branch from the terminal.
  7. Delete the local branch too.
  8. Run git branch -a again — are both gone?

Questions to answer:

  • What command pushes a local branch to the remote and sets up tracking?
  • What command deletes a remote branch?
  • After you delete the remote branch, does the local branch also disappear?

Answer — Question 10

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
git checkout -b feature-contact
echo "<h1>Contact Us</h1>" > contact.html
git add contact.html
git commit -m "Add contact page"

git push -u origin feature-contact
git branch -a

git push origin --delete feature-contact
git branch -d feature-contact

git branch -a

git branch -a after push:

1
2
3
4
* feature-contact
  main
  remotes/origin/feature-contact
  remotes/origin/main

git branch -a after deletion:

1
2
  main
  remotes/origin/main

Command to push branch with tracking:git push -u origin <branch-name>

Command to delete a remote branch:git push origin --delete <branch-name>

After remote deletion:The local branch is NOT automatically deleted. They are independent. You delete the local branch separately with git branch -d <name>.


Question 11 — Merge Conflicts

Scenario:Two branches have modified the same line of the same file. Git cannot auto-merge and needs your decision.

Tasks — execute in order:

  1. Navigate back to my_project (not the clone). Ensure you are on main.
  2. Create a file message.txt with the content "Welcome to our site" and commit it.
  3. Create branch branch-a. Change message.txt to "Welcome to our amazing product!" and commit.
  4. Switch back to main. Create branch branch-b. Change message.txt to "Discover what we can do for you." and commit.
  5. Switch to main. Merge branch-a — this should succeed.
  6. Now merge branch-b — this should produce a conflict.
  7. Run git status — what does it show?
  8. Open message.txt — write down exactly what the conflict markers look like.
  9. Resolve the conflict by choosing the best version (or writing a new one). Remove all conflict markers.
  10. Mark the file as resolved and complete the merge.
  11. Run git log --oneline --graph to see the merge commit.

Questions to answer:

  • What are the three conflict markers and what does each one mean?
  • What command do you run to mark a file as resolved after editing it?
  • How do you abort a merge that is in progress and return to the state before the merge?

Answer — Question 11

Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cd my_project
git checkout main

echo "Welcome to our site" > message.txt
git add message.txt
git commit -m "Add welcome message"

git checkout -b branch-a
echo "Welcome to our amazing product!" > message.txt
git add message.txt
git commit -m "Update welcome message - branch A"

git checkout main
git checkout -b branch-b
echo "Discover what we can do for you." > message.txt
git add message.txt
git commit -m "Update welcome message - branch B"

git checkout main
git merge branch-a

git merge branch-b

Expected conflict output:

1
2
3
Auto-merging message.txt
CONFLICT (content): Merge conflict in message.txt
Automatic merge failed; fix conflicts and then commit the result.

git status shows:

1
2
3
4
5
6
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   message.txt

cat message.txt shows the conflict markers:

1
2
3
4
5
<<<<<<< HEAD
Welcome to our amazing product!
=======
Discover what we can do for you.
>>>>>>> branch-b

Resolving:

1
2
3
4
5
6
7
8
9
10
11
# Edit message.txt to contain only your chosen content, e.g.:
echo "Welcome — discover our amazing product today!" > message.txt

# Verify no conflict markers remain
cat message.txt

# Mark as resolved
git add message.txt

# Complete the merge
git commit

The three conflict markers:

  • <<<<<<< HEAD — start of YOUR current branch’s version
  • ======= — dividing line between the two versions
  • >>>>>>> branch-name — end of the INCOMING branch’s version

Command to mark resolved:git add <filename> — staging the file tells Git “I have fixed this conflict.”

Abort a merge in progress:

1
git merge --abort

Returns your working directory to exactly how it was before the git merge command.


Bonus Challenge — Full Week 1 Workflow

Without any hints, complete the following from scratch. Write your exact commands.

Scenario:You are building a small portfolio website.

  1. Create a fresh repository portfolio_project with git init.
  2. Configure your name and email if not already set.
  3. Create index.html with content "<h1>My Portfolio</h1>", stage and commit it.
  4. Create .gitignore that ignores *.log files, commit it.
  5. Create a debug.log file — confirm it is ignored by Git.
  6. Create a feature branch feature-about.
  7. On that branch, create about.html with "<h1>About Me</h1>" and commit.
  8. Switch back to main and add a "<footer>2026</footer>" line to index.html and commit. (This makes main and feature-about diverge.)
  9. Merge feature-about into main — this should produce a three-way merge commit.
  10. Create branch feature-contact, create contact.html, commit it.
  11. Accidentally commit an empty file oops.txt on feature-contact.
  12. Use git reset --soft HEAD~1 to undo the bad commit and unstage oops.txt.
  13. Complete the correct commit for contact.html only.
  14. Merge feature-contact into main.
  15. Push main to a GitHub remote.
  16. Final check: git log --oneline --graph should show a clean history with two merge commits.

Answer — Bonus Challenge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
mkdir portfolio_project && cd portfolio_project && git init

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

echo "<h1>My Portfolio</h1>" > index.html
git add index.html
git commit -m "Add index page"

echo "*.log" > .gitignore
git add .gitignore
git commit -m "Add .gitignore to exclude log files"

touch debug.log
git status   #debug.log should NOT appear — it is ignored

git checkout -b feature-about
echo "<h1>About Me</h1>" > about.html
git add about.html
git commit -m "Add about page"

git checkout main
echo "<footer>2026</footer>" >> index.html
git add index.html
git commit -m "Add footer to index"

git merge feature-about
# Three-way merge — both branches had new commits since they diverged

git checkout -b feature-contact
echo "<h1>Contact Me</h1>" > contact.html
git add contact.html
git commit -m "Add contact page"

touch oops.txt
git add oops.txt
git commit -m "accidentally added oops"

git reset --soft HEAD~1
git restore --staged oops.txt
git commit -m "Add contact page"

git checkout main
git merge feature-contact

git remote add origin <your-remote-url>
git push -u origin main

git log --oneline --graph

Expected final git log --oneline --graph:

1
2
3
4
5
6
7
8
9
10
11
*   Merge branch 'feature-contact'
|\
| * Add contact page
|/
*   Merge branch 'feature-about'
|\
| * Add about page
* | Add footer to index
|/
* Add .gitignore to exclude log files
* Add index page

Good luck! If your output matches the expected results, you have mastered Week 1.

This post is licensed under CC BY 4.0 by the author.