dev branch so main
stays safeIf you write code, notes, configs, or scripts, you will want two things sooner or later:
Git does the first part. It lives on your machine and remembers versions of your files.
GitHub does the second part. It stores Git projects on the internet so you can back them up, share them, or work with other people.
This guide walks you from nothing to a project on GitHub, using only the terminal. The Git commands are the same on Mac and Windows. What changes is how you install Git and which terminal you open.
By the end you should be able to:
notes-cliPaths look a bit different. On Mac you often see /Users/yourname/.... On Windows you might see
C:\Users\yourname\..., or in Git Bash
/c/Users/yourname/.... The Git commands
stay the same.
If you use Homebrew:
brew install gitIf you do not use Homebrew, Apple’s Command Line Tools also install Git:
xcode-select --installThen check:
git --versionYou should see a line that starts with git version.
Check:
git --versionThis guide uses Git Bash on Windows. If you use
PowerShell instead, the Git commands still work. Some small shell
helpers like ls or touch may differ.
Press Cmd + Space, type Terminal, press Enter. Or open
Applications → Utilities → Terminal.
Open Git Bash from the Start menu.
Go to a folder where you keep projects:
# Mac example
cd ~/Developer
# Windows Git Bash example
cd ~/DocumentsIf the folder is not there yet:
mkdir -p ~/Developer
cd ~/DeveloperIn PowerShell on Windows you can use:
mkdir $HOME\Developer
cd $HOME\DeveloperDo this once on each computer.
Every save (commit) stores a name and email. Use the email on your GitHub account, or the noreply email GitHub shows you in your email settings.
git config --global user.name "Your Name"
git config --global user.email "you@example.com"git config --global init.defaultBranch main
git config --global pull.rebase false
git config --global core.editor "nano"What those do, in plain words:
maingit pull will merge by default (not
rebase), which is simpler when you are learningnano is the editor Git opens if it
needs oneOn Windows, if you prefer Notepad or VS Code:
git config --global core.editor notepad
# only if the `code` command works in your terminal
git config --global core.editor "code --wait"git config --global --listYou need a GitHub account
first. Then pick one way to prove who you are when you upload (git push) or download updates (git pull / git fetch).
Install the gh tool:
# Mac with Homebrew
brew install gh
# Windows PowerShell with winget
winget install --id GitHub.cliYou can also download it from cli.github.com.
Log in:
gh auth loginAnswer the questions on screen. A common path is:
Check that it worked:
gh auth statusGitHub does not accept your normal account password for Git over
HTTPS. If you are not using gh, you create
a Personal Access Token and use that when Git asks for
a password.
git push asks for a password,
paste the tokenThis is another common long-term setup. You make a key pair on your
computer, add the public key to GitHub, then use SSH URLs like git@github.com:USER/REPO.git.
ssh-keygen -t ed25519 -C "you@example.com"Press Enter to accept the default file location unless you know you want something else.
Then (Mac / Git Bash):
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# Mac: copy the public key
pbcopy < ~/.ssh/id_ed25519.pub
# Windows Git Bash: copy the public key
clip < ~/.ssh/id_ed25519.pubOn GitHub: Settings → SSH and GPG keys → New SSH key → paste → save.
Test:
ssh -T git@github.comGitHub should reply with a message that includes your username.
For the sample project below, this guide uses HTTPS with GitHub CLI, because that is usually the shortest path for beginners on both Mac and Windows.
You do not need to memorize everything. Learn this loop first:
| Command | Plain meaning |
|---|---|
git status |
Show what changed and what is ready to save |
git add <file> |
Mark a file to include in the next save |
git add . |
Mark everything in this folder |
git commit -m "message" |
Save a snapshot on your computer |
git push |
Upload your commits to GitHub |
git pull |
Download updates from GitHub and bring them into your current branch |
git log --oneline |
Show a short list of past saves |
git branch |
List branches |
git switch -c name |
Create a branch and move to it |
git switch name |
Move to an existing branch |
git clone <url> |
Download an existing project |
You may also see git checkout -b name.
That older form also creates a branch and switches to it. Both are real.
This guide mostly uses git switch.
Other useful checks:
git diff # changes not marked with git add yet
git diff --staged # changes already marked with git add
git restore file.txt # throw away local edits to a tracked file
git restore --staged file.txt # unmark a file, but keep your editsBe careful with git restore file.txt.
It can discard work you have not committed.
We will make a tiny project from scratch, put it on GitHub, then practice a feature branch.
The project is a small shell script that saves dated notes into a file. It is small on purpose, so you can finish the Git steps in one sitting.
cd ~/Developer # or any folder you use for code
mkdir notes-cli
cd notes-cligit initGit should say it created a repository. That means this folder now has history tracking.
Create README.md:
cat > README.md << 'EOF'
# notes-cli
A tiny command-line notes helper.
## Usage
```bash
./note.sh "Buy milk"
./note.sh "Ship the blog draft"
```
Notes are appended to `notes.txt`.
EOFCreate note.sh:
cat > note.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
NOTE="${*:-}"
if [ -z "$NOTE" ]; then
echo "Usage: ./note.sh \"your note\""
exit 1
fi
STAMP="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$STAMP] $NOTE" >> notes.txt
echo "Saved: $NOTE"
EOFOn Mac and in Git Bash, make the script runnable:
chmod +x note.shCreate .gitignore so common junk files
are not saved:
cat > .gitignore << 'EOF'
.DS_Store
Thumbs.db
*.tmp
EOFWindows note: if the cat ... << 'EOF' style feels awkward in
PowerShell, create the same files in an editor with the same names and
contents, then continue with the Git commands.
git status
git add README.md note.sh .gitignore
git status
git commit -m "Initial commit: notes-cli script and README"If Git says it does not know who you are, go back to First-time setup.
See the history:
git log --onelineFrom inside notes-cli, if you installed
gh and signed in:
gh repo create notes-cli --public --source=. --remote=origin --pushThat command:
notes-cli
under your accountorigin as the remote name for
GitHubmain)If you prefer the website:
notes-cli.gitignore
there, because you already have them locallygit remote add origin https://github.com/YOUR_USERNAME/notes-cli.git
git push -u origin mainReplace YOUR_USERNAME with your real
GitHub username.
Open it in the browser:
gh repo view --webOr visit https://github.com/YOUR_USERNAME/notes-cli.
./note.sh "First note from my machine"
./note.sh "Learning Git on the terminal"
cat notes.txtThe script creates notes.txt. For this
sample we will track that file too:
git add notes.txt
git commit -m "Add sample notes file from first runs"
git pushA branch is a separate line of work. You can change files there
without changing main until you merge.
git switch -c feature/list-commandNow update note.sh so ./note.sh --list prints the notes:
cat > note.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
if [ "${1:-}" = "--list" ] || [ "${1:-}" = "-l" ]; then
if [ ! -f notes.txt ]; then
echo "No notes yet."
exit 0
fi
cat notes.txt
exit 0
fi
NOTE="${*:-}"
if [ -z "$NOTE" ]; then
echo "Usage: ./note.sh \"your note\""
echo " ./note.sh --list"
exit 1
fi
STAMP="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$STAMP] $NOTE" >> notes.txt
echo "Saved: $NOTE"
EOFchmod +x note.sh
./note.sh --listSave and upload the branch:
git add note.sh
git commit -m "Add --list flag to print saved notes"
git push -u origin feature/list-commandOpen a pull request:
gh pr create --title "Add list command" --body "Adds ./note.sh --list to print notes.txt"Or on GitHub, after the push, click Compare & pull request.
Merge it on GitHub, or with:
gh pr merge --mergeThen update main on your computer:
git switch main
git pull origin mainThat is the basic loop: work locally → commit → push → open a pull request → merge → pull.
dev branch so main
stays safeOn many projects, main is the branch
you want to keep stable. That means: finished work only. If you build
half-done features straight on main, it
gets messy. Your own unfinished edits can get mixed with work you wanted
to keep clean.
A common pattern is:
main for work you trustdev branch for day-to-day
buildingdev into mainSome teams name that middle branch develop instead of dev. Same idea.
| Branch | Usual job |
|---|---|
main |
Stable copy |
dev |
Place where you build features |
feature/... |
Optional short branch for one change, often merged into dev first |
This does not make merge conflicts impossible. Conflicts can still
happen when two branches change the same lines. What it
does do is keep unfinished work off main until you choose to bring it in.
Create dev
once
git switch main
git pull origin main
git switch -c dev
git push -u origin devDo new work on dev
Example: stay on dev, make a small
change, commit, and push dev only.
git switch dev
git pull origin devEdit a file. For a concrete example, add one line to README.md, then:
git status
git add README.md
git commit -m "Note that development happens on the dev branch"
git push origin devmain still has the older version until
you merge.
Optional: make a feature branch from dev
If the change is bigger, create a feature branch from dev:
git switch dev
git pull origin dev
git switch -c feature/readme-note
# edit, then:
git add .
git commit -m "Explain the dev branch in the README"
git push -u origin feature/readme-note
gh pr create --base dev --title "Explain dev branch" --body "Adds a short note to the README"--base dev means the pull request goes
into dev, not main.
When dev is ready,
merge it into main
Do this when the work on dev is in good
shape.
With GitHub CLI:
gh pr create --base main --head dev --title "Merge dev into main" --body "Ready set of notes-cli updates"
gh pr merge --mergeOr on the website: Pull requests → New → base main, compare dev
→ create → merge.
Or merge on your computer:
git switch main
git pull origin main
git merge dev
git push origin mainAfter that, it is often useful to update dev so it includes what is now on main:
git switch dev
git merge main
git push origin devIf Git reports a merge conflict
Git stops and marks the files. Open them and look for markers like:
<<<<<<<
your change
=======
the other change
>>>>>>>
Keep the right code, delete the markers, then:
git add .
git commit
git pushIf you are in the middle of a merge and Git already prepared a merge
commit message, git commit with no -m is enough after you fix the files.
A simple rule
main cleandevdev (or on a feature branch
that merges into dev)dev into mainThat is what people usually mean by “don’t do all your work on main.”
After the repo exists, a calm day often looks like this:
cd ~/Developer/notes-cli
git switch dev
git pull origin dev
# edit files
git status
git add .
git commit -m "Say why you made the change"
git push origin devWhen a batch of work is ready, merge dev into main
using the steps in the section above.
If you do not have a dev branch yet and
only need a tiny fix on main:
git switch main
git pull
# make the small fix
git add .
git commit -m "Say why you made the change"
git pushCommit messages are easier to read later if they explain why:
Fix crash when notes.txt is missingUpdate filesIf you are joining someone else’s project instead of creating your own:
git clone https://github.com/SOMEUSER/SOME-REPO.git
cd SOME-REPOorigin pointsgit remote -vgit restore note.shgit restore --staged note.shgit stash
git pull
git stash popgit stash saves uncommitted work
temporarily. git stash pop brings it back.
If pop causes conflicts, fix those files
the same way you fix merge conflicts.
| Task | Mac / Git Bash | Windows PowerShell |
|---|---|---|
| Go home | cd ~ |
cd $HOME |
| List files | ls |
ls or dir |
| Make a bash script runnable | chmod +x note.sh |
use Git Bash for that script style |
| Copy an SSH public key | pbcopy < ~/.ssh/id_ed25519.pub |
in Git Bash: clip < ~/.ssh/id_ed25519.pub |
Permission denied (publickey) — your
SSH key is missing from the agent or from GitHubAuthentication failed on HTTPS — use a
token or gh auth login, not your GitHub
account passwordrejected (non-fast-forward) on push —
someone else (or another computer) has commits you do not have yet; run
git pull, fix any conflicts, then push
againPlease tell me who you are — set user.name and user.emailYou do not need every Git command on day one. You need a loop you trust:
status → add → commit → push
and enough comfort with pull, branches,
and remotes that the terminal is not scary.
On Mac and Windows, Git’s words are the same. Install Git, set your
name, sign in to GitHub, then walk a real folder like notes-cli from git init to a merge.
A useful habit after that: keep unfinished work on dev or a feature branch, and only merge into
main when you mean to.