Git, Docker, and CI/CD Foundations · Lesson 1 of 10

Free preview

Version control, and your first commits

Create a Git repository, make commits, read history and diffs, understand the three areas, and keep generated files out with .gitignore.

time
55 min
on completion
+110 XP

Why this matters

Every team that writes software needs to answer three questions all day long: what changed, who changed it, and can we go back? Without a tool, people answer them with folders named app-final, app-final-2, and app-really-final, and with a chat history that nobody can reconstruct. A version control system answers those questions properly. It records the exact content of your files at each point you choose, remembers who recorded it and why, and lets you compare or return to any of those points.

Git is the version control system almost the entire industry uses. It is not tied to any website: a Git repository is just a hidden .git directory inside your project that holds the complete history. GitHub and GitLab are hosting products built *around* Git, but you can do real work — commits, branches, merges, history — with nothing but the git command on your own machine. That is exactly how this course starts. You will build one small HTTP service on your lab machine linux01 and carry it all the way to a released, containerised application. This first lesson gets the service under version control.

By the end you will have a repository with four commits, a running Python service, and a clear mental model of the three places your work lives before it becomes permanent history.

Concepts

A repository is a project directory plus the .git folder that stores its history. You create one with git init. Nothing leaves your machine.

A commit is a snapshot: the exact content of the tracked files at the moment you record it, plus a message, an author, and a link to the commit before it. Commits are the units of history. Each has a unique 40-character hash (for example f48ab710…); the first seven characters are usually enough to name it.

Git has three areas, and understanding them removes most early confusion:

  • The working tree is the files as they exist on disk right now — what you edit.
  • The staging area (also called the *index*) is a holding area where you assemble exactly what your next commit will contain.
  • The repository is the permanent history of commits.

You move a change from the working tree to the staging area with git add, and from the staging area into history with git commit. This two-step design is deliberate: it lets you commit *some* of your changes and leave others for later.

git status tells you where everything sits — untracked, modified, staged. git diff shows the actual line-by-line changes: with no arguments it shows working-tree changes not yet staged; with --staged it shows what you have staged for the next commit. git log shows the history.

A `.gitignore` file lists paths Git should not track — generated files, caches, secrets. Ignoring a file keeps it out of history and out of git status noise.

Guided exercise

Open a terminal on your lab machine linux01. Confirm the tools and versions:

bash
head -2 /etc/os-release
git --version
python3 --version
whoami
text
PRETTY_NAME="Ubuntu 24.04.4 LTS"
NAME="Ubuntu"
git version 2.43.0
Python 3.12.3
ubuntu

1. Identify yourself. Git stamps every commit with a name and email. Set them once:

bash
git config --global user.name "Ada Learner"
git config --global user.email "ada.learner@example.com"
git config --global init.defaultBranch main

Use your own name and a contact email; the address is a label on your commits, not a login. init.defaultBranch main makes new repositories start on a branch called main.

2. Create the repository.

bash
mkdir -p ~/app
cd ~/app
git init
text
Initialized empty Git repository in /home/ubuntu/app/.git/

3. Make the first commit. Create a README, then watch it move through the three areas:

bash
cat > README.md <<'EOF'
# app

A small HTTP status service used throughout this course.
EOF
git status
git add README.md
git commit -m "Add README"

git status first calls README.md an *untracked* file. After git add it is a *change to be committed* (staged). After git commit it is history:

text
[main (root-commit) f48ab71] Add README
 1 file changed, 3 insertions(+)
 create mode 100644 README.md

4. Add the service and commit it.

bash
mkdir -p service
cat > service/server.py <<'EOF'
"""A tiny HTTP service. GET / returns a plain-text greeting."""
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        body = b"app is running\n"
        self.send_response(200)
        self.send_header("Content-Type", "text/plain")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)


if __name__ == "__main__":
    ThreadingHTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
EOF
git add service/server.py
git commit -m "Add minimal HTTP service"

Confirm the service actually runs, then stop it:

bash
python3 service/server.py >/tmp/server.log 2>&1 & echo $! >/tmp/server.pid; sleep 1
curl -s http://127.0.0.1:8080/
kill "$(cat /tmp/server.pid)"
text
app is running

5. See the difference between the working tree and the staging area. Add run instructions to the README, then inspect:

bash
printf "\n## Run it\n\n    python3 service/server.py\n" >> README.md
git diff
git add README.md
git diff
git diff --staged
git commit -m "Document how to run the service"

The first git diff shows your change with + lines. After git add, git diff shows *nothing* — because it reports the working tree against the staging area, and they now match. git diff --staged shows the change, because it compares the staging area against the last commit. This is the single most useful thing to internalise: `git diff` is unstaged changes, `git diff --staged` is what you are about to commit.

6. Ignore generated files. Running Python created a __pycache__ directory. It is build output, not source, so it should never be committed:

bash
python3 -m py_compile service/server.py
git status
cat > .gitignore <<'EOF'
__pycache__/
*.pyc
.venv/
.env
EOF
git check-ignore -v service/__pycache__/server.cpython-312.pyc
git add .gitignore
git commit -m "Ignore Python caches and local environment files"

git check-ignore -v proves *which* rule matches a path:

text
.gitignore:1:__pycache__/	service/__pycache__/server.cpython-312.pyc

Note that .env is ignored from the start; you will store secrets there later, and they must never enter history.

Review your work:

bash
git log --oneline
git log --oneline | wc -l
text
986ae57 Ignore Python caches and local environment files
380a6e7 Document how to run the service
e582e06 Add minimal HTTP service
f48ab71 Add README
4

Troubleshooting

Symptom → Author identity unknown when you commit. Cause: user.name/user.email are unset. Fix: run the two git config --global commands in step 1; the email is a label, so any address you own is fine.

Symptom → nothing to commit, working tree clean but you just edited a file. Cause: you edited a file that is ignored, or you edited it in a different directory. Fix: run git status; if the file is missing from the output, check git check-ignore -v FILE.

Symptom → you committed __pycache__ before adding .gitignore. Cause: .gitignore only stops *untracked* files from being added; it does not remove already-tracked ones. Fix: git rm -r --cached service/__pycache__ then commit; the files stay on disk but leave history going forward.

Symptom → git commit opens an editor you do not know how to exit. Cause: you ran git commit without -m. Fix: in the default editor nano, save with <kbd>Ctrl</kbd>+<kbd>O</kbd> then exit with <kbd>Ctrl</kbd>+<kbd>X</kbd>; next time pass -m "message".

Symptom → curl: (7) Failed to connect. Cause: the server is not running, or exited. Fix: check /tmp/server.log; a SyntaxError there means the file has a typo — fix it and rerun.

Check your understanding

Take the Chapter 1 quiz after lesson 4. Meanwhile, confirm you can answer these:

What is the difference between git add and git commit?

git add copies the current content of a file into the staging area, choosing what the next commit will contain. git commit records everything staged as a permanent snapshot with a message. You can add several times before one commit.

You edited two files but want to commit only one. How?

git add only the file you want, then git commit. git status and git diff --staged confirm exactly what is going in before you commit.

Summary and next step

  • A Git repository records snapshots (commits) of your project on your own machine; no account is needed.
  • Changes flow working tree → staging area (git add) → repository (git commit); git status and git diff/git diff --staged show you where things are.
  • .gitignore keeps generated files and secrets out of history.

Next, in lesson 2, you will branch to develop a feature in isolation, merge it back, and resolve a merge conflict by hand.

References

  • Git — Getting Started and Basics (Pro Git) — https://git-scm.com/book/en/v2 (accessed 2026-09-13)
  • git-scm.com — git status — https://git-scm.com/docs/git-status (accessed 2026-09-13)
  • git-scm.com — gitignore — https://git-scm.com/docs/gitignore (accessed 2026-09-13)
  • Python 3.12 — http.server — https://docs.python.org/3.12/library/http.server.html (accessed 2026-09-13)

Sign in to record your progress

Signing in saves eligible progress. It does not enroll you or include a lab; review the career path for access terms.

Sign in