Turning a Folder into a Repository
A repository — everyone says "repo" — is just an ordinary folder that Git has been told to watch. There are exactly two ways to get one: run git init inside a folder to start tracking it, or run git clone to copy a repository that already exists somewhere else. That is the whole list.
git init does something smaller than beginners imagine. It creates one hidden subfolder called .git and changes nothing else. Your files are not moved, copied or altered. Nothing is committed yet. From this moment Git simply has somewhere to record history when you ask it to.
That .git folder is the repository. Every commit you ever make, every branch, every configured remote lives inside it. Copying your project folder copies the history along with it, because the history is inside. Deleting .git deletes your entire history in one stroke and leaves you with a plain folder of files — there is no confirmation prompt and no undo, so treat that folder as untouchable. You never need to open it by hand.
mkdir college-portfolio
cd college-portfolio
git init
# Initialized empty Git repository in .../college-portfolio/.git/
git status
# On branch main
#
# No commits yet
#
# nothing to commit (create/copy files and use "git add" to track)
ls -a
# . .. .git <- the repository itself is that hidden folder - Never run
git initin your home directory or on your Desktop. Git would then try to track every file on your computer, andgit statusbecomes an unusable wall of text. If you do it by accident, delete the.gitfolder that was just created in that location — your own files are untouched. - For the same reason, do not create a repository inside another repository. Git will track the outer one and behave confusingly about the inner one.
Reading git status Properly
git status is the command you will run more than any other, and it repays being read carefully rather than skimmed. It answers four questions at once: which branch you are on, which files are staged and ready to commit, which tracked files you have modified but not staged, and which files Git has never seen before.
That last group — untracked files — is where new users get stuck. Git does not automatically watch a new file just because it appeared in the folder. It waits for you to say git add. This is deliberate: you do not want compiled output, editor settings and downloaded libraries silently joining your project history. But it does mean a file you created five minutes ago is invisible to Git until you say otherwise.
Notice that the output tells you what to do next. Under staged changes it suggests git restore --staged to undo staging; under unstaged changes it suggests git add and git restore. Git is unusually good about this, and reading those hints rather than searching the internet will teach you the commands faster.
echo "# College Portfolio" > README.md
mkdir css && echo "body { margin: 0; }" > css/style.css
git status
# On branch main
#
# No commits yet
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# README.md
# css/
#
# nothing added to commit but untracked files present
# The compact version, once you know what the letters mean
git status --short
# ?? README.md <- ?? untracked
# ?? css/
# A index.html <- A added (staged)
# M style.css <- M modified, not staged
# MM app.js <- staged AND modified again since staging Your First Commit
Committing is a two-step move by design: stage what you want, then record it. git add copies the current state of a file into the staging area; git commit writes everything staged into permanent history as one snapshot with a message attached.
Write the message for a stranger, because in three months that stranger is you. "update", "changes" and "asdf" are the three most common first commit messages, and every one of them makes your history worthless when you are hunting for the commit that broke the login page. "Add project README and base stylesheet" takes four extra seconds and is the difference between a history you can search and a list of noise.
After committing, run git log and look at what Git stored: a hash, your name and email from the configuration you set earlier, a timestamp, and your message. Run git status again and it now says the working tree is clean, meaning your folder and the last commit are identical. Getting back to a clean working tree is the natural rhythm of working with Git.
git add README.md css/style.css
git status --short
# A README.md
# A css/style.css
git commit -m "Add project README and base stylesheet"
# [main (root-commit) 9f4c2a1] Add project README and base stylesheet
# 2 files changed, 2 insertions(+)
git log
# commit 9f4c2a1b8e5d3f0a... (HEAD -> main)
# Author: Ananya Sharma <ananya@example.com>
# Date: Sat Aug 1 11:04:22 2026 +0530
#
# Add project README and base stylesheet
git status
# On branch main
# nothing to commit, working tree clean - If Git replies
Please tell me who you are, you have not setuser.nameanduser.emailyet. Go back to the setup lesson, set them globally, and run the commit again — nothing was lost.
The .gitignore File
Not everything in your project folder belongs in your history. node_modules/ can contain tens of thousands of files that anyone can regenerate with one command. Compiled output changes on every build and creates enormous meaningless diffs. Editor folders describe your setup, not the project. And .env files contain passwords and API keys that must never be published.
A file named .gitignore, placed at the root of your repository, lists patterns for the things Git should pretend not to see. Files matching those patterns stop appearing as untracked in git status, and git add . quietly skips them. Create this file before your first real commit — it is far easier than cleaning up afterwards.
The pattern syntax is small. A plain name matches that file anywhere in the project. A trailing slash means "this is a directory". A leading slash anchors the pattern to the repository root. * matches any run of characters within one path segment, and a leading ! un-ignores something a previous line ignored. You almost never need to write one from scratch: GitHub offers ready-made .gitignore templates for every common language when you create a repository, and github.com/github/gitignore holds the full collection.
Commit the .gitignore file itself. It is part of the project's configuration, and every teammate who clones the repository should get the same rules automatically.
# .gitignore — commit this file into the repository
# Dependencies (regenerate with npm install)
node_modules/
vendor/
# Secrets — never commit these
.env
.env.local
*.pem
config/secrets.json
# Build output
dist/
build/
*.class
__pycache__/
# Editor and OS clutter
.vscode/
.idea/
.DS_Store
Thumbs.db
# Logs and databases
*.log
*.sqlite3
# Ignore every .txt file, but keep this one
*.txt
!requirements.txt logs/— a directory called logs, anywhere in the project/build— only thebuildentry at the repository root, notsrc/build*.log— every file ending in.logtemp/*.log— log files directly insidetemp, but not in its subfolders!important.log— an exception to an earlier ignore rulegit check-ignore -v filename— tells you exactly which line of which file is ignoring something, which is the fastest way to debug a rule that is not behaving
The Gotcha: .gitignore Does Not Untrack Existing Files
This is the single most common .gitignore misunderstanding, and it is worth reading twice. .gitignore only affects files Git is not already tracking. Once a file has been committed even once, Git has taken responsibility for it, and adding its name to .gitignore afterwards changes nothing at all. The file will keep showing up as modified, and every future change to it will keep getting committed.
The scenario plays out like this every semester. A student commits their project, node_modules/ and all, then discovers the mistake and adds node_modules/ to .gitignore. The folder keeps appearing in git status. They conclude that .gitignore is broken. It is not — it was simply never in charge of that folder.
The fix is git rm --cached, which removes a file from Git's tracking while leaving it untouched on your disk. The --cached flag is doing the important work here: without it, git rm deletes the real file too. Add -r to handle a directory. After that, the .gitignore rule finally takes effect, and your next commit records the removal.
One important limit: this stops the file being tracked from now on. It does not erase it from the commits already made. Anyone who checks out an older commit still gets it. If what you accidentally committed was a password or an API key, untracking it is not enough — that case is covered properly in the Best Practices lesson, and the short version is that you must change the secret itself.
# Wrong: added to .gitignore but still tracked, so still committed
git status
# modified: node_modules/express/index.js
# Right: stop tracking it, keep it on disk
git rm --cached -r node_modules/
# Same idea for a single file
git rm --cached .env
# Now .gitignore takes over. Record the removal.
git commit -m "Stop tracking node_modules and .env"
# DANGEROUS variant — this deletes the real file from your disk:
# git rm .env <- no --cached, the file is gone
# Which rule is ignoring this file?
git check-ignore -v .env
# .gitignore:5:.env .env git rm --cachedkeeps your local file;git rmwithout it deletes your local file. When you are dealing with an.envthat holds real credentials, that difference is the difference between a tidy repository and an afternoon of re-creating configuration from memory.
