Folders named final, final-v2, and final-really are a sign you needed snapshots, not more copies. Git records those snapshots in order so you can undo a bad edit, compare what changed, and share the same history with other people (or your future self).
You do not need branching, rebases, or pull requests on day one. You need one loop: change files, review the diff, stage what belongs together, commit, and push. The sections below walk that loop end to end — with enough context that each command feels intentional, not magical.
Warm-up: mental model and a playground
Git thinks in three places. The working tree is the files you edit. The staging area (also called the index) is a holding zone for the next snapshot. The commit history is the permanent record of snapshots you chose to keep. Most beginner confusion is mixing those three up — git status exists to show which area each change sits in.
Confirm Git is installed before you start:
git --version
If that fails, install Git from your OS package manager or git-scm.com, then run the check again.
Create a throwaway lab folder so you can experiment without touching a real project:
mkdir git-basics-lab
cd git-basics-lab
Use this directory for every example below. When the article ends, you can delete the folder.
Already have a project online?
If the code already lives on GitHub or GitLab, you usually clone instead of starting from an empty folder. Clone copies the remote history and sets origin for you:
git clone https://github.com/OWNER/REPO.git
cd REPO
Then skip ahead to making changes — you already have a repository. The rest of this article follows the init path so you see every step once.
Initialize and name the branch
Turn the lab folder into a repository. git init creates a hidden .git directory that stores history; it does not upload anything:
git init
Many hosts expect the default branch to be named main. Rename it now so your first push matches that convention:
git branch -M main
Ask Git what it sees:
git status
On a fresh repo you should get something like:
On branch main
No commits yet
nothing to commit (create/copy files and use "git add" to track)
Note: git status is the map. When you are unsure what to run next, run status first — it tells you whether changes are untracked, unstaged, staged, or already committed.
Tell Git who you are
Every commit records an author. Set your name and email once on this machine (use the email tied to your GitHub or GitLab account if you have one):
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
Check the values Git will use:
git config --global --get user.name
git config --global --get user.email
Note: --global writes to your user config for all repos on this machine. Omit --global only when you need a different identity inside one project.
Make a change and inspect the diff
Create a small file so there is something to track:
echo 'Day one notes' > notes.txt
git status
Git should list notes.txt under Untracked files. Untracked means Git sees the file but has never been asked to include it in a snapshot.
After a file is tracked, later edits show up as modifications. Add a second line, then inspect the line-by-line diff before staging:
echo 'Remember to commit often' >> notes.txt
git diff
git diff with no arguments shows unstaged changes in tracked files. New untracked files do not appear in git diff until you add them once — status still lists them.
Note: Read the diff before you stage. Catching a secret, a typo, or an accidental delete is cheaper here than in a commit message apology.
Stage, review staged, and unstage safely
Staging selects what goes into the next commit. Add one file by name:
git add notes.txt
git status
notes.txt should move under Changes to be committed. Review exactly what is staged:
git diff --staged
To stage everything in the current directory (new files and edits), use:
git add .
Prefer named paths when the directory is messy; use git add . when you intentionally want the whole tree.
Unstaging puts the file back in the working tree without deleting your edits. That is the safe “I staged too much” undo:
git restore --staged notes.txt
git status
The file returns to untracked or modified, but notes.txt on disk is unchanged.
Note: git restore --staged only touches the staging area. Your local work stays until you discard it on purpose (next section).
Discard unstaged edits (destructive)
If you edited a tracked file and want to throw those edits away — returning the file to the last committed version — restore the working tree:
git restore notes.txt
To discard unstaged edits in every tracked file under the current directory:
git restore .
Note: This permanently drops uncommitted work in those files. There is no recycle bin. Prefer file-scoped git restore notes.txt until you are sure. Untracked files are not removed by git restore; remove those yourself or with a deliberate clean command later.
For this walkthrough, put the content back if you discarded it, then stage again before committing:
echo 'Day one notes' > notes.txt
echo 'Remember to commit often' >> notes.txt
git add notes.txt
Ignore junk early
Some files should never enter history: OS clutter, logs, build output, local env files. Create a .gitignore in the repo root:
cat > .gitignore <<'EOF'
.DS_Store
*.log
EOF
Create a dummy log so you can see ignore working:
echo 'noise' > debug.log
git status
debug.log should stay out of the untracked list. Stage the ignore file so the rule is shared with anyone who clones later:
git add .gitignore
git status
Note: Patterns in .gitignore only affect untracked files. If you already committed a secret or a huge binary, ignoring it afterward is not enough — you must remove it from history with a separate cleanup. Start ignore rules early to avoid that mess.
Commit and read history
A commit freezes the staged snapshot and attaches a message. Keep the message short and imperative — say why the change exists:
git commit -m "Add notes and ignore local logs"
Inspect recent history in a compact form:
git log --oneline -n 5
You should see one line with a short hash and your message, for example:
a1b2c3d Add notes and ignore local logs
Note: Prefer many small commits with clear messages over one giant “wip” dump. Future you will search this log when something breaks.
Connect a remote and push
A remote is a named URL where Git can send and fetch commits. Create an empty repository on GitHub or GitLab (no README if you already committed locally), then link it as origin:
git remote add origin https://github.com/OWNER/REPO.git
git remote -v
git remote -v should print fetch and push URLs for origin. Push your main branch and set upstream tracking so later pushes can be just git push:
git push -u origin main
Authentication depends on how you clone: HTTPS usually prompts once and stores a credential; SSH uses a key you set up with the host. Either works — pick one and stay consistent.
Deploy hosts such as Cloudflare Pages typically pull from that same GitHub or GitLab remote. You push to the source host; the deploy service watches the branch. You rarely set Cloudflare itself as origin.
Note: If the remote already has commits you do not have locally (for example a README created on the website), the first push may be rejected. For a true empty remote, -u origin main is enough. Syncing divergent histories is a later topic.
Quick reference card
Keep this nearby until the loop becomes muscle memory:
| Goal | Command |
|---|---|
| Check install | git --version |
| Copy a remote repo | git clone <url> |
| Start a local repo | git init |
| Rename branch to main | git branch -M main |
| See workspace state | git status |
| Set identity | git config --global user.name "..." / user.email "..." |
| Unstaged diff | git diff |
| Stage a file / all | git add <file> / git add . |
| Staged diff | git diff --staged |
| Unstage (keep edits) | git restore --staged <file> |
| Discard unstaged edits | git restore <file> |
| Commit | git commit -m "..." |
| Recent history | git log --oneline -n 5 |
| Add remote | git remote add origin <url> |
| List remotes | git remote -v |
| First push | git push -u origin main |
Practice drills
Stay inside git-basics-lab (or recreate it) and try these without peeking. The point is to choose the command from the state git status reports.
- Create
todo.txtwith one line, confirm it is untracked, then stage only that file. - Edit
todo.txtagain, view the unstaged diff, then stage the new edit and view the staged diff. - Unstage
todo.txtwithout losing the file contents, then stage and commit it with a clear message. - Add
tmp/to.gitignore, createtmp/cache.bin, and confirm status hides it. - Show the last three commits in one-line form.
When you are ready to compare, here are solid answers — not the only ones, but clear and portable:
echo 'buy milk' > todo.txt
git status
git add todo.txt
echo 'buy eggs' >> todo.txt
git diff
git add todo.txt
git diff --staged
git restore --staged todo.txt
git add todo.txt
git commit -m "Track todo list"
echo 'tmp/' >> .gitignore
mkdir -p tmp && echo x > tmp/cache.bin
git add .gitignore
git status
git log --oneline -n 3
If you can work through those five comfortably, you already cover most day-one Git work: init or clone, inspect, stage carefully, commit with intent, ignore noise, and push when a remote exists. When push fails because someone else changed the same file — or you need to undo a commit — continue with When Push Fails: Merge Conflicts, Revert, and Rollback.