Chapter 2 · Git and GitHub
Version Control and Your First Repository
- Page 4 of 22
- 14 min read
Every AI project eventually has a folder like this: train.py, train_v2.py, train_v2_fixed.py, train_FINAL.py, and a notebook whose good version was overwritten last Tuesday. Nobody knows which file produced the model that is running in production, or what changed between the version that scored 0.91 and the one that scores 0.84. Version control solves this: it records every saved state of your project, who made it and why, and lets you compare or return to any of them.
Git is the version control system almost everyone uses, from solo learners to the teams building the largest AI models. This page explains what it does, sets it up, and walks you through your first repository and your first commits, on a small project that will grow into a sentiment-analysis API.
What you will learn
- What version control is and why AI work needs it
- The difference between Git and GitHub
- How to configure Git once: your name, email and default branch
- How to turn a folder into a repository with
git init - The three areas — working tree, staging area, repository — and the
status→add→commitcycle
What version control gives you
| Without version control | With Git |
|---|---|
Copies named _v2, _final, _final2 | One set of files; every saved state (a commit) is in the history |
| "What changed since yesterday?" — nobody knows | git diff shows every changed line |
| A bad change means rewriting from memory | Return any file, or the whole project, to any earlier commit |
| Teammates email zip files and overwrite each other | Everyone works on their own copy and Git merges the changes |
| "Which code produced this model?" | Each result can be tagged with the exact commit that produced it |
Git is distributed: every copy of a repository contains the complete history, so you can commit, browse history and undo mistakes with no network at all. Sharing with others is a separate step (pushing to a server) that you will learn in GitHub: Remotes, push, pull and clone.
Git and GitHub are different things
| Git | GitHub | |
|---|---|---|
| What it is | A program on your computer | A website and service that hosts Git repositories |
| Made by | Linus Torvalds (2005), now an open-source community project | GitHub, Inc., owned by Microsoft |
| Works offline | Yes, completely | No |
| Gives you | History, commits, branches, merges | A shared copy online, pull requests and code review, issues, GitHub Actions (CI/CD), project pages |
| Alternatives | (Mercurial, SVN — rare in new projects) | GitLab, Bitbucket, Codeberg, Azure DevOps |
You can use Git your whole life without GitHub. GitHub without Git makes no sense: it is a place to put Git repositories. This page is all Git; GitHub comes in chapter 2's later pages.
Set up Git once
Install Git as shown in Environment Variables, .env Files and Virtual Environments (on Windows, Git for Windows also gives you Git Bash). Every commit records an author name and email, so tell Git who you are. --global means "for every repository of this user"; it is stored in the file ~/.gitconfig:
git config --global user.name "Nadia Rahman"
git config --global user.email "nadia@example.com"
git config --global init.defaultBranch mainThe third line needs a word of explanation. A repository's first branch (a line of history — you will meet branches properly in Branches, Merging and Merge Conflicts) gets a name when the repository is created. Git itself still calls it master unless you say otherwise and prints a long hint about it; GitHub, GitLab and most teams have used main for years. Setting init.defaultBranch makes your repositories match. Read the settings back to check them:
git config --global user.name
git config --global user.email
git config --global init.defaultBranchNadia Rahman
nadia@example.com
mainA few more settings worth knowing:
- Email and GitHub: use the email address of your GitHub account so your commits are linked to your profile. If you do not want your address public, GitHub gives you a private "noreply" address in Settings → Emails; use that one here.
- Editor: when Git needs a longer message it opens an editor. The default is often Vim, which confuses newcomers (to escape: press
Esc, type:q!, press Enter). To use VS Code instead:git config --global core.editor "code --wait"; for a simple terminal editor,nano. - Line endings on Windows: Windows ends lines with CRLF, Linux and macOS with LF. The Git for Windows installer sets
core.autocrlf=true, which converts automatically; keep that default.
Git 3.0 is coming. The Git project has announced its next major version. It will make
mainthe default branch name, use the stronger SHA-256 hash for new repositories (instead of SHA-1) and store branch information in a faster format. There is no release date yet, and your day-to-day commands will not change; configuringinit.defaultBranch mainnow simply gets you there early. Any Git from 2.51 on (current: 2.56) is fine for this course.
Your first repository
A repository (repo) is a project folder whose history Git tracks. Create the project folder and turn it into one with git init:
mkdir -p ~/projects/sentiment-api
cd ~/projects/sentiment-api
git init
ls -aInitialized empty Git repository in /home/nadia/projects/sentiment-api/.git/
.
..
.gitThe only visible change is a hidden folder, .git. That folder is the repository: every commit, branch and setting lives inside it. Your files stay in the project folder around it, as normal files you edit with any editor. Two rules follow: never edit files inside .git by hand, and never delete it — deleting .git deletes the whole history and leaves only the current files.
git status is the command you will type most. It tells you which branch you are on and what Git sees:
git statusOn branch main
No commits yet
nothing to commit (create/copy files and use "git add" to track)Working tree, staging area, repository
Git moves your changes through three areas. This picture is the key to almost every Git command:
working tree staging area repository (.git)
the files you edit what goes into the the saved history:
next commit (the "index") commits
+------------------+ +------------------+ +------------------+
| README.md | ---> | README.md | ----> | commit 2 |
| sentiment.py | git | | git | commit 1 |
| (edited) | add | | commit| |
+------------------+ +------------------+ +------------------+
^ |
+----------------- git restore / git switch ----------+
(get earlier versions back)- The working tree is your project folder as it is right now, including unsaved experiments.
- The staging area (also called the index) is where you assemble the next commit.
git addcopies the current state of a file there. - The repository holds the commits.
git committurns whatever is staged into a new, permanent snapshot.
Why the middle step? Because you often change several things at once — fix a bug, tweak a prompt, try a new threshold — and want to save them as separate, meaningful commits. The staging area lets you choose exactly what goes into each one.
The status → add → commit cycle
Create two files: a README and a tiny rule-based sentiment classifier, our stand-in for a real model:
cat > README.md <<'EOF'
# Sentiment API
A tiny sentiment classifier for course reviews, growing into a web API.
EOF# file: sentiment.py
"""A tiny rule-based sentiment classifier: our stand-in for a real model."""
POSITIVE = {"good", "great", "clear", "love", "loved"}
NEGATIVE = {"bad", "hard", "slow", "confusing"}
def classify(text: str) -> str:
words = set(text.lower().replace(",", " ").split())
score = len(words & POSITIVE) - len(words & NEGATIVE)
if score > 0:
return "positive"
if score < 0:
return "negative"
return "neutral"
if __name__ == "__main__":
for review in ["Great course, loved it", "Setup was hard", "It was okay"]:
print(f"{review!r}: {classify(review)}")python3 sentiment.py
git status'Great course, loved it': positive
'Setup was hard': negative
'It was okay': neutral
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
sentiment.py
nothing added to commit but untracked files present (use "git add" to track)The program works. Git, however, calls both files untracked: they are in the folder, but Git is not recording them yet. Stage just the README and look again:
git add README.md
git statusOn branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
sentiment.pyNow the README is under "Changes to be committed" — it is staged — while sentiment.py is still untracked. Git even prints the command to take a file back out of the staging area. Commit what is staged, with a message saying what the commit does:
git commit -m "Add README"[main (root-commit) faa9787] Add README
1 file changed, 3 insertions(+)
create mode 100644 README.mdRead the output line by line: the commit is on branch main; root-commit means it is the first one, with no parent; the seven characters after it are the start of the commit's unique ID (its hash — you will see what it is computed from in Commits, History and Diffs); then the message and a summary of the change. create mode 100644 means a new, ordinary (non-executable) file.
Now the classifier, then check the state and the history:
git add sentiment.py
git commit -m "Add rule-based sentiment classifier"
git status
git log --oneline[main 7563c84] Add rule-based sentiment classifier
1 file changed, 20 insertions(+)
create mode 100644 sentiment.py
On branch main
nothing to commit, working tree clean
7563c84 Add rule-based sentiment classifier
faa9787 Add README"Nothing to commit, working tree clean" is the state to aim for before you stop for the day or switch tasks: everything you care about is saved in a commit. git log --oneline lists the commits, newest first.
Changing a tracked file
Once a file is tracked, Git notices every change to it. Add a usage section to the README:
cat >> README.md <<'EOF'
## Usage
python3 sentiment.py
EOF
git status
git status -sOn branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
M README.mdThe file is modified but not staged. git status -s is the short form: two columns, the first for the staging area and the second for the working tree, so M means "modified, not staged yet", M "modified and staged", A "new file, staged" and ?? "untracked". Stage and commit it:
git add README.md
git status -s
git commit -m "Document how to run the classifier"
git log --onelineM README.md
[main 3b80ca4] Document how to run the classifier
1 file changed, 4 insertions(+)
3b80ca4 Document how to run the classifier
7563c84 Add rule-based sentiment classifier
faa9787 Add READMEWays to stage more than one file at a time:
| Command | Stages |
|---|---|
git add file1 file2 | Just those files |
git add . | Every change in the current folder and below, including new files |
git add -A | Every change in the whole repository, including deletions |
git commit -a -m "…" | Skips staging for files Git already tracks (new files are ignored) |
git add . is convenient and also the easiest way to commit something you did not mean to: a .env with your API key, a 2 GB dataset, a .venv folder. Always run git status before committing, and learn .gitignore: Keeping Secrets and Junk Out before your first real project.
Where this shows up in AI and backend work
- Reproducible experiments: experiment trackers such as MLflow and Weights & Biases record the Git commit of the code that trained each model, so "which code produced this?" always has an answer.
- Prompts are code: in LLM applications the system prompt, the few-shot examples and the model settings live in files in the repository, so a change to the prompt is reviewed and can be rolled back like any code change.
- Deployments: every CI/CD system (GitHub Actions in the advanced tutorial) builds and deploys a specific commit; rolling back a bad release means deploying an earlier one.
- Working with AI coding assistants: commit before you let an assistant change many files at once; if the result is wrong, Git gets you back in one command.
Common mistakes
- Running
git initin the wrong folder. In your home folder it turns everything you own into one huge repository. Alwayscdinto the project first and runpwd. If it happened, delete that.gitfolder (and only that one). - Committing without staging.
git commitsaves only what is staged, so after editing a file and forgettinggit addyou get:echo "Reviews come from the BitByte courses." >> README.md git commit -m "Describe the data"Stage the file, then commit:On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md no changes added to commit (use "git add" and/or "git commit -a")git add README.md git commit -m "Describe the data"[main 542bc11] Describe the data 1 file changed, 1 insertion(+) - Running Git outside a repository.
fatal: not a git repository (or any of the parent directories): .gitmeans your current folder is not inside a repository — usually you are in the wrong folder. - Committing without telling Git who you are. Without
user.nameanduser.email, Git either refuses to commit ("Author identity unknown") or guesses an address from your computer's name. Set both once, as above. - Waiting too long between commits. One giant commit at the end of the week cannot be partly undone and tells no story. Commit each small step that works.
Try it yourself
- Easy: Create a new repository
~/projects/prompt-librarycontaining a filesummarise.txtwith the lineSummarise the text below in three bullet points., commit it with the messageAdd summarise prompt, and show the log in one-line form. - Medium: In the same repository, create two new files,
translate.txtandclassify.txt(any one-line prompt each). Commit onlytranslate.txt, then show the short status. - Hard: From a subfolder of
sentiment-api, find the repository's top folder withgit rev-parse --show-toplevel. Then run the same command from your home folder and look at the exit code withecho $?.
Answers
1. A fresh repository, one file, one commit:
mkdir -p ~/projects/prompt-library
cd ~/projects/prompt-library
git init -q
echo "Summarise the text below in three bullet points." > summarise.txt
git add summarise.txt
git commit -q -m "Add summarise prompt"
git log --oneline77befcd Add summarise prompt(-q, quiet, hides the usual messages; leave it out if you want to see them.)
2. Stage by name to choose what goes into the commit; the other file stays untracked:
echo "Translate the text below into Bangla." > translate.txt
echo "Label the review below as positive, negative or neutral." > classify.txt
git add translate.txt
git commit -q -m "Add translate prompt"
git status -s?? classify.txt3. Git looks for .git in the current folder and then in each parent folder, so Git commands work from anywhere inside a repository. Outside one, it fails with exit code 128:
mkdir -p ~/projects/sentiment-api/data
cd ~/projects/sentiment-api/data
git rev-parse --show-toplevel
cd ~
git rev-parse --show-toplevel
echo "exit code: $?"/home/nadia/projects/sentiment-api
fatal: not a git repository (or any of the parent directories): .git
exit code: 128Summary
- Version control records every saved state of a project with who, when and why; Git is the standard tool, and every copy holds the full history.
- Git is the program; GitHub is a hosting service for Git repositories with collaboration features on top.
- Configure once:
user.name,user.emailandinit.defaultBranch main(the default Git 3.0 will adopt). git initcreates the.gitfolder, which is the repository. Changes move from the working tree to the staging area (git add) to the history (git commit).git statusbefore every commit; commit small, working steps.
Next: Commits, History and Diffs shows how to write commit messages people can use, how to read the history with git log and git show, how to see exactly what changed with git diff, and what a commit really is inside.