diff --git a/README.md b/README.md index 9f5f03a..48eab59 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,5 @@ Personal reference docs for tools I use regularly. ## Tools -- [rclone](rclone.md) — File syncing and copying between local storage and - remote destinations. SFTP, object storage, and 60+ backends. +- [git](git.md) — Version control. Tracking changes, committing, pushing to Forgejo, undoing mistakes. +- [rclone](rclone.md) — File syncing and copying between local storage and remote destinations. SFTP, object storage, and 60+ backends. diff --git a/git.md b/git.md new file mode 100644 index 0000000..da288a6 --- /dev/null +++ b/git.md @@ -0,0 +1,172 @@ +# git + +## What is it + +git is a distributed version control system. It tracks changes to files over +time, lets you move between states (commits), and makes it safe to experiment +because nothing is ever truly lost. + +The mental model that makes everything click: git is a timeline of snapshots. +Each commit is a complete snapshot of your project at a point in time, with a +pointer to the commit before it. That's it. Branches are just named pointers +to commits. Everything else follows from this. + +## What it is mainly used for + +- Tracking changes to files over time with a full history +- Safely experimenting — you can always go back +- Pushing code or content to a remote (like Forgejo) for safekeeping +- Collaborating with others (or your future self) on the same codebase + +## Core concepts + +**Repository (repo)** — a folder that git is tracking. The `.git/` directory +inside it holds the entire history. Don't touch `.git/` directly. + +**Commit** — a snapshot of your files at a point in time. Every commit has +a unique hash (e.g. `f649b2b`) and a message describing what changed. + +**Branch** — a named pointer to a commit. `main` is the default branch. +Creating a branch lets you work on something without touching `main`. + +**Remote** — a copy of the repo somewhere else. In your case, Forgejo. +Conventionally named `origin`. + +**Staging area** — a holding area between your working files and a commit. +You explicitly choose what goes into each commit using `git add`. + +## Setting up a new repo + +```bash +cd ~/my-project +git init +git add . +git commit -m "Initial commit" +git remote add origin git@vps-forgejo-01.warthog-rockhopper.ts.net:egeidal/my-project.git +git push -u origin main +``` + +The `-u` flag sets `origin/main` as the upstream — after this, a plain +`git push` is enough. + +## Daily workflow + +The loop you'll use constantly: + +```bash +# 1. Check what has changed +git status + +# 2. Stage the files you want to commit +git add filename.md # specific file +git add . # everything changed + +# 3. Commit with a message +git commit -m "Add rclone SSH key section" + +# 4. Push to Forgejo +git push +``` + +## Useful commands + +### Seeing what's going on + +```bash +git status # what's changed, what's staged +git log # full commit history +git log --oneline # compact one-line history +git diff # unstaged changes +git diff --staged # staged changes (what will be committed) +``` + +### Undoing things + +```bash +# Unstage a file (undo git add, keep the changes) +git restore --staged filename.md + +# Discard changes to a file entirely (cannot be undone) +git restore filename.md + +# Amend the last commit (message or content) +git commit --amend --no-edit # keep message, update content +git commit --amend -m "Better message" # change message + +# Undo the last commit, keep the changes staged +git reset --soft HEAD~1 + +# Undo the last commit, keep the changes unstaged +git reset HEAD~1 +``` + +### Working with remotes + +```bash +git remote -v # show configured remotes +git remote add origin # add a remote +git push # push current branch +git push -u origin main # push and set upstream +git pull # fetch + merge from remote +git fetch # fetch without merging +git clone # clone a remote repo locally +``` + +### Branches + +```bash +git branch # list branches +git branch my-feature # create a branch +git switch my-feature # switch to a branch +git switch -c my-feature # create and switch in one step +git merge my-feature # merge branch into current branch +git branch -d my-feature # delete a branch +``` + +## .gitignore + +A `.gitignore` file tells git which files to never track. Put it in the root +of your repo. + +``` +# Example .gitignore +.DS_Store +*.log +secrets.env +node_modules/ +``` + +Files already tracked by git won't be ignored just by adding them here — you +need to untrack them first: + +```bash +git rm --cached filename +``` + +## Useful flags + +| Flag | What it does | +|------|-------------| +| `--oneline` | Compact log output — one commit per line | +| `--all` | Show all branches in log | +| `--graph` | Show branch/merge history as ASCII graph | +| `--soft` | Reset keeping changes staged | +| `--amend` | Modify the last commit | +| `--no-edit` | Keep the existing commit message when amending | +| `-u` | Set upstream remote when pushing | +| `-v` | Verbose — show more detail (e.g. with `git remote -v`) | +| `--cached` | Apply command to staging area only | +| `--force` | Force push — overwrites remote history. Use with care | + +## Notes + +- `git push --force` rewrites remote history. Fine on your own private repos, + destructive in shared ones. Use `--force-with-lease` as a safer alternative. +- Commit messages are for your future self. "Fix stuff" is useless in six + months. "Fix rclone SSH key format in guide" is not. +- Commit early and often. Small commits are easier to understand and undo + than one giant commit with everything in it. +- The staging area feels annoying at first but is actually useful — it lets + you make two unrelated changes and commit them separately with clear messages. +- If something goes wrong, `git status` and `git log --oneline` are always + your first move. Understand the state before doing anything.