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:
- Create a new directory called
my_projectand navigate into it. - Create an empty file called
notes.txt. - Verify the file exists using
ls. - Initialize a Git repository in this directory.
- Run
git statusand write down what Git says aboutnotes.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:
- Stage
notes.txtusing the appropriategit addcommand. - Run
git status— what has changed compared to Question 1? - Commit the staged file with the message
"initial commit". - Run
git statusagain — what does Git say now? - Run
git log --onelineand 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:
- Add the text
"first line"tonotes.txtusingecho(overwrite, not append). - Run
git status— what does Git say aboutnotes.txtnow? - Run
git diff notes.txt— what does the output show? - Stage
notes.txtwithgit add. - Run
git diff notes.txtagain — what happens? Why? - Run
git diff --staged notes.txt— what does this show? - Commit with message
"added first line". - Append
"second line"tonotes.txt(use>>). - Stage and commit with message
"added second line". - Run
git log --oneline --graph --decorate— how many commits do you see?
Questions to answer:
- What is the difference between
git diffandgit diff --staged? - What is the difference between
>and>>withecho? - What do the
+and-symbols mean ingit diffoutput?
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:
- Create a file called
debug.log. - Run
git status— isdebug.logvisible to Git? - Create a
.gitignorefile with the pattern*.loginside it. - Run
git statusagain — what happened todebug.log? - Create another file
error.log— does it also disappear fromgit status? - Stage and commit
.gitignorewith an appropriate commit message.
Questions to answer:
- What is the purpose of
.gitignore? - What does the
*wildcard mean in a.gitignorepattern? - Name three types of files that should always be in
.gitignorein 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:
- Secret/credential files (
.env,secrets.yml,*.pem) — never commit passwords or API keys - Build artifacts / compiled output (
dist/,build/,*.pyc,node_modules/) - 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:
- Create two files:
feature.pyandaccident.py. - Stage bothfiles with
git add. - Run
git status— confirm both are staged. - Unstage only
accident.pyusing the modern commandgit restore --staged. - Run
git status— what is the state of each file now? - Commit only
feature.pywith message"add feature file". - Confirm
accident.pyis 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>andgit 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:
- Stage and commit
accident.py(from Q5) with message"oops committed accident". - Verify the commit exists with
git log --oneline. - Use
git reset --soft HEAD~1to undo the last commit. - Run
git status— where isaccident.pynow? - Run
git log --oneline— is the commit still there? - Unstage
accident.pywithgit restore --staged accident.py. - Now commit a new file
another_mistake.pywith message"another mistake". - Use
git reset --mixed HEAD~1(or justgit reset HEAD~1) to undo it. - Run
git status— where isanother_mistake.pynow? - Commit a new file
delete_this.pywith message"delete this entirely". - Use
git reset --hard HEAD~1to undo it completely. - Run
git statusAND check ifdelete_this.pystill exists on disk withls.
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:
- Open a new file in vi:
vi practice.txt - 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
- Return to Normal mode (press
Esc). - Save the file without quitting (
:wthen Enter). - Navigate to line 1 using
gg. - Navigate to line 3 using
G. - Delete line 3 using
dd. - Undo the deletion using
u. - Save and quit with
:wq. - Verify the content:
cat practice.txt
Now try the Git commit editor:
- Make a change to any file and stage it.
- Run
git commit(WITHOUT the-mflag) — vi opens. - Press
i, type your commit message on the first line. - Press
Esc, then:wqto save. - 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
:qand: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.
dddeletes a line,ggjumps to top, etc. PressEscto get here from any other mode. - Insert mode: Keypresses type text normally. Press
ito enter. PressEscto return to Normal. - Command-line mode: Appears at the bottom of the screen after pressing
:. Run commands like save, quit, search. PressEnterto execute,Escto 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:
- Create an empty repository on GitHub — do NOT tick “Add README”.
- Copy the SSH URL of the remote repository.
- Link your local
my_projectto the remote:git remote add origin <URL> - Verify:
git remote -v - Push all local commits:
git push -u origin main - Run
git status— what does Git say about your branch now? - Using the GitHub web interface, create a new file
team_notes.txtand commit it (simulating a teammate’s push). - In your terminal, run
git fetch. - Run
git diff main..origin/main— what does this show? - Run
git pullto sync. - Run
git diff main..origin/mainagain — what is the output now?
Questions to answer:
- What does the
-uflag ingit push -u origin maindo? - What is the difference between
git fetchandgit pull? - Why is it better to run
git fetchthengit diffbeforegit 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 fetchdownloads remote commits into remote-tracking branches (likeorigin/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:
- Clone the
my_projectrepository to a NEW folder calledmy_project_clone:1
git clone <your-remote-url> my_project_clone
- Navigate into
my_project_cloneand rungit log --oneline— what do you see? - Create a new branch called
feature-aboutand switch to it in one command. - Create a file
about.htmland add content:"<h1>About Us</h1>". - Stage and commit with message
"Add about page". - Run
git log --oneline --graph --decorate— what does the output show about the branches? - Switch back to
mainand checkgit log --oneline— is theabout.htmlcommit there? - Run
lsonmain— isabout.htmlvisible? - Merge
feature-aboutintomain. - Run
git log --oneline --graph— what type of merge happened (fast-forward or three-way)? - Delete the
feature-aboutbranch.
Questions to answer:
- What does
git clonedo? 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
originto point back at the source URL - Checks out the default branch
- Sets up upstream tracking
Fast-forward vs three-way merge:
- Fast-forward:
mainhas not changed since the branch was created. Git simply movesmainforward to the branch tip — no extra merge commit. - Three-way merge: Both
mainand 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 ingit 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:
- In
my_project_clone, create a new branchfeature-contact. - Create
contact.htmlwith the content"<h1>Contact Us</h1>"and commit it. - Push the branch to the remote:
git push -u origin feature-contact - Verify on GitHub that the branch appears.
- Run
git branch -a— what do you see? - Now delete the remote branch from the terminal.
- Delete the local branch too.
- Run
git branch -aagain — 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:
- Navigate back to
my_project(not the clone). Ensure you are onmain. - Create a file
message.txtwith the content"Welcome to our site"and commit it. - Create branch
branch-a. Changemessage.txtto"Welcome to our amazing product!"and commit. - Switch back to
main. Create branchbranch-b. Changemessage.txtto"Discover what we can do for you."and commit. - Switch to
main. Mergebranch-a— this should succeed. - Now merge
branch-b— this should produce a conflict. - Run
git status— what does it show? - Open
message.txt— write down exactly what the conflict markers look like. - Resolve the conflict by choosing the best version (or writing a new one). Remove all conflict markers.
- Mark the file as resolved and complete the merge.
- Run
git log --oneline --graphto 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.
- Create a fresh repository
portfolio_projectwithgit init. - Configure your name and email if not already set.
- Create
index.htmlwith content"<h1>My Portfolio</h1>", stage and commit it. - Create
.gitignorethat ignores*.logfiles, commit it. - Create a
debug.logfile — confirm it is ignored by Git. - Create a feature branch
feature-about. - On that branch, create
about.htmlwith"<h1>About Me</h1>"and commit. - Switch back to
mainand add a"<footer>2026</footer>"line toindex.htmland commit. (This makesmainandfeature-aboutdiverge.) - Merge
feature-aboutintomain— this should produce a three-way merge commit. - Create branch
feature-contact, createcontact.html, commit it. - Accidentally commit an empty file
oops.txtonfeature-contact. - Use
git reset --soft HEAD~1to undo the bad commit and unstageoops.txt. - Complete the correct commit for
contact.htmlonly. - Merge
feature-contactintomain. - Push
mainto a GitHub remote. - Final check:
git log --oneline --graphshould 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.