Command Palette

Search for a command to run…

ZarynZaryn
·12 min read·

Version control in pictures

What version control is, and what a repository, commit, diff, branch, merge, conflict, push, pull and pull request are, explained with pictures you can play step by step. No experience needed.

New to all this? Start here. Each idea below has a picture first and a few lines of text after it. Pictures with numbered steps play by themselves: press **▶** to watch again, or tap a step to stop on it. Tap any commit or device to read more about it. > **Tip:** you don't need to remember the commands yet. Get the picture first. The [Quick start](/docs/en/quickstart) has you typing them in five minutes. ## Why version control exists ```diagram type: cards title: Without it, and with it --- 😩 | report_final_v2_REALLY_final.docx | Copies everywhere, and nobody knows which one is right | bad 🕳️ | "It worked yesterday" | No way to see what changed since, or to go back | bad 📨 | Emailing files back and forth | Two people edit the same file, one person's work is lost | bad 🕰️ | Every saved version, forever | One project folder; go back to any moment you saved | good 🔍 | Exactly what changed | Line by line, who changed it and why | good 🤝 | Work together safely | Everyone works on their own line, then the work is combined | good ``` A **version control system** (VCS) is a tool that remembers every version of your project you choose to save, so you can see what changed, go back, and work with other people without overwriting each other. **zev** is a version control system; **Zaryn** is the website where you share what zev saves. Think of a video game's save points: you save before the hard part, and if things go wrong, you load the save. Version control gives your project save points, each with a note saying what you did. ## A repository: your project, with a memory ```diagram type: graph title: What's inside a repository height: 56 alt: A project folder holds your files and a hidden history; the history holds every commit --- node folder | my-site/ | 12 24 | folder | accent | The repository: an ordinary folder you work in node f1 | index.html | 42 6 | file | | A file you edit, like any other node f2 | style.css | 42 20 | file | | Another file node f3 | photo.jpg | 42 34 | file | | Big files too: pictures, data, models node hist | history | 42 47 | log | good | Hidden inside the folder: every saved version of every file, with who saved it and why node c | 128 commits | 74 47 | commit | | Each save point is a commit link folder f1 link folder f2 link folder f3 link folder hist | hidden | ghost link hist c | | path ``` A **repository** (or *repo*) is a project folder that zev looks after. It looks like a normal folder full of your files, plus a hidden history of every version you saved. You make one with `zev init` in a folder you have, `zev new` to start fresh, or `zev clone` to download one from Zaryn. ## Three places your work lives ```diagram type: flow title: From an edit to a save point loop: edit, add, commit, again and again --- Working folder | The files you see and edit. zev notices what's changed | | ✏️ ~ zev add Staging area | The changes you've picked for the next save point | accent | 📥 ~ zev commit History | A save point, kept forever, with your message | good | 🕰️ ``` Why the middle step? Say you fixed a bug and also started a new page. `zev add` lets you put just the bug fix in this save point and keep the half-done page for the next one, so each save point is one clear change. `zev status` tells you what's in each place at any moment. ## A commit: one save point ```diagram type: cards title: What one commit holds --- 📸 | A snapshot | Every file as it was when you committed, not just the changes | accent 💬 | A message | Your note: "Add a contact form". Write it for your future self | 👤 | Who and when | Your name, your Zaryn account, the date and time | ⬅️ | Its parent | The commit before it: that's how commits form a history | #️⃣ | A fingerprint | A hash made from all of the above, like `7f3a9c2`. Change one letter and it changes | good ``` A **commit** is one save point. Commits line up, each pointing back at the one before, and that line is your project's **history**. Watch a few being made: ```diagram type: history title: A history, one commit at a time start: A new repository caption: Each dot is a commit. The tag above the newest one is the branch name, main, which always moves to your latest commit. --- branch main | accent commit A main | First page | info Made with `zev commit -m "First page"` step Commit | You add a style sheet and commit. B points back at A. commit B main | Add colours | info Its parent is A step Commit | You fix a typo and commit again. commit C main | Fix a typo | info Its parent is B step Commit | A contact form. History grows to the right; nothing before is ever changed. commit D main | Contact form | info The newest commit: main points here step Tag a release | You give C a name that never moves, v1.0, so you can always find the version you published. tag C | v1.0 mark C | accent ``` `zev log` lists the history; `zev show` shows one commit in full. Because every commit is a full snapshot with a fingerprint, you can always get any of them back exactly as it was, and nobody can quietly change an old one. ## A diff: what changed ```diagram type: diff title: The changes between two commits file: index.html --- @@ -3 +3 @@ <body> - <h1>Welcom to my site</h1> + <h1>Welcome to my site</h1> > The typo fix: one line removed (red), its new version added (green) <p>Hello!</p> + <a href="contact.html">Contact me</a> > A brand new line: only green </body> ``` A **diff** is the difference between two versions: red lines were removed, green lines were added, and plain lines are unchanged context so you can find your place. A changed line shows up as one red and one green. Try **Side by side** above to see the old and new versions next to each other. - `zev diff` shows what you changed but haven't staged. - `zev diff --staged` shows what's in the staging area, the next commit. - `zev show` shows the diff a commit made. zev also understands code: a [semantic diff](/docs/en/semantic-diff) tells you when a change only moved or reformatted code, so you can review what really changed. ## A branch: try things without breaking anything ```diagram type: history title: A branch is a separate line of work start: main only --- branch main | accent branch new-search | good | from C commit A main | First page commit B main | Add colours commit C main | Fix a typo | info The commit the branch starts from step zev checkout -b new-search | A new name at C. Nothing is copied: a branch is just a name pointing at a commit. step Commit on the branch | You build a search box. main doesn't have D; your live site is untouched. commit D new-search | Search box | info Only on new-search step Keep going | Another commit on the branch. commit E new-search | Search results | info Only on new-search step Meanwhile on main | Someone fixes the footer on main. Two lines of work now, side by side. commit F main | Fix footer | info Only on main ``` A **branch** is a named line of commits. Every repository starts with `main`. When you want to try something, make a branch: your commits go on it, and `main` stays exactly as it was until you decide to bring the work in. Switch between branches with `zev checkout <branch>`; your files change to match. See [Branches and merging](/docs/en/branches). ## A merge: bring the work back When the branch is ready, you **merge** it into `main`. There are two cases. **If `main` didn't move**, merging just moves the name `main` forward to the branch's newest commit (a *fast-forward*): ```diagram type: history title: Merge when main didn't change start: Before --- branch main | accent branch new-search | good | from C commit A main | First page commit B main | Add colours commit C main | Fix a typo commit D new-search | Search box commit E new-search | Search results step zev merge new-search | On main. Nothing new happened on main, so main simply moves to E. Done. point main | E ``` **If `main` moved too**, zev puts `main`'s own new commits back on top of the branch's, so the history stays one straight line with both sides' work: ```diagram type: history title: Merge when both sides changed start: Before --- branch main | accent branch new-search | good | from C commit A main | First page commit B main | Add colours commit C main | Fix a typo commit D new-search | Search box commit E new-search | Search results commit F main | Fix footer | info main's own new commit step zev merge new-search | zev replays F on top of E as a new commit, F′: the same change, a new place in history. commit F′ main | Fix footer | after E | copy F | info The same change as F, replayed after E mark F | faded step The result | `main` is now `A → B → C → D → E → F′`: both the search and the footer fix, in one line. mark F′ | good ``` ## A conflict: when both changed the same lines ```diagram type: diff title: What a conflict looks like in the file file: index.html --- <h1> + <<<<<<< main + Welcome to my site + ======= + Welcome! Search below + >>>>>>> new-search > zev marks the spot: main's version on top, the branch's version below </h1> ``` If `main` and your branch changed **the same lines**, zev can't guess which one you want, so it stops and changes nothing. That's a **conflict**, and it's normal. You fix it by hand: ```diagram type: flow title: Fixing a conflict --- Mark | `zev merge new-search --resolve` marks every place both sides changed | warn | ⚠️ ~ edit Choose | Keep one side, the other or a mix; delete the marker lines | | ✏️ ~ zev add Stage | Tell zev the file is fixed | | ➕ ~ zev merge --continue Done | The merge finishes | good | ✓ ? Changed your mind? `zev merge --abort` puts everything back ``` Merging often keeps conflicts small and rare. The full walkthrough is in [Branches and merging](/docs/en/branches#when-both-changed-the-same-lines). ## Your computer and Zaryn: clone, push, pull ```diagram type: sequence title: One project, two computers, one place in between lanes: You | Zaryn | A teammate alt: The teammate clones from Zaryn; you push commits; they pull them and push their own; you pull those back --- == Getting a copy == A teammate -> Zaryn : zev clone Zaryn --> A teammate : the whole history == Sharing your work == You -> Zaryn : zev push: your new commits note Zaryn : every commit is checked against its fingerprint | good A teammate -> Zaryn : zev pull Zaryn --> A teammate : only the commits they don't have == Getting theirs == A teammate -> Zaryn : zev push You -> Zaryn : zev pull ``` - **Clone**: download a repository, with its whole history, to your computer. - **Push**: send your new commits up to Zaryn. - **Pull**: get commits other people pushed, and update your files. Your computer has the full history too, so you can commit, look at the history and make branches without internet. You only need a connection to push and pull. More in [Pushing and pulling](/docs/en/pushing-and-pulling). ## A pull request: ask before you merge ```diagram type: flow title: The life of a pull request --- Branch | Do the work on a branch and push it | | 🌿 ~ zev pr Pull request | "Please merge new-search into main", with its diff | accent | 📬 ~ review and checks Review | Teammates comment; CI runs the tests | warn | 👀 ? Changes asked? Push more commits: the pull request updates itself ~ approve Merge | The work lands on main | good | ✓ ``` A **pull request** (PR) asks to merge one branch into another, with room to talk first. It shows the diff, runs the [CI checks](/docs/en/memoized-ci), and lets teammates comment on any line. When everyone's happy, someone presses **Merge** (or runs `zev pr merge <number>`). On your own project it's still useful: a PR is a checklist before your work goes live. ## Two more you'll meet ```diagram type: cards --- 🏷️ | Tag | A name that stays on one commit forever, like `v1.0`, for releases. `zev tag v1.0` | accent 🧺 | Stash | Put unfinished changes aside to switch branches, and take them back later. `zev stash`, `zev stash pop` | ↩️ | Restore | Undo changes to a file you haven't committed. `zev restore <file>` | 🍴 | Fork | Your own copy of someone else's repository on Zaryn, to change freely and send back with a PR | ``` ## The whole picture ```diagram type: graph title: Everything on this page, together height: 58 alt: You edit files, add and commit them to your local history, push to Zaryn, open a pull request, and teammates pull your work --- node edit | Your files | 8 22 | file | | The working folder: edit freely node stage | Staging | 26 22 | folder | | `zev add` picks changes for the next commit node hist | Your history | 44 22 | log | accent | `zev commit` saves a snapshot; branches and merges happen here, offline node zaryn | Zaryn | 66 22 | cloud | good | `zev push` shares commits; pull requests, reviews and CI live here node mate | A teammate | 90 22 | laptop | | `zev clone` and `zev pull` bring your work to them node pr | Pull request | 66 46 | branch | warn | Review the diff, run checks, merge link edit stage | zev add | path link stage hist | zev commit | path link hist zaryn | zev push | path link zaryn mate | zev pull | path2 link zaryn pr | review ``` ## Words in one line each | Word | In one line | Command | |---|---|---| | Repository | A project folder with its full history | `zev init`, `zev clone` | | Commit | One save point: a snapshot, a message, who and when | `zev commit -m "…"` | | Staging area | The changes picked for the next commit | `zev add` | | History | All commits, each pointing at the one before | `zev log` | | Diff | What changed between two versions, red and green | `zev diff` | | Branch | A named line of commits for separate work | `zev checkout -b <name>` | | Merge | Bring one branch's work into another | `zev merge <branch>` | | Conflict | Both sides changed the same lines; you choose | `zev merge --resolve` | | Clone / push / pull | Download / send / get commits from Zaryn | `zev clone`, `zev push`, `zev pull` | | Pull request | Ask for a review before merging | `zev pr` | | Tag | A fixed name for one commit, for releases | `zev tag` | Ready? Follow the [Quick start](/docs/en/quickstart), keep the [words list](/docs/en/glossary) nearby, and when you're stuck, [How do I…?](/docs/en/how-do-i) has short answers.