t0ul

Automate the Boring Stuff with Go, Ch. 0.3: git in an Afternoon

Part of Automate the Boring Stuff with Go, a beginner-friendly guide to programming in Go.

git is how programmers keep the history of their files. Not a backup, not a sync folder: a history, where every step you choose to save is kept forever, with a note saying what it was, and you can look at or return to any of them. Every program in this book lives in a git repository, and by chapter 16 you will be publishing your own.

git has a reputation for being hard. The whole of it is hard. The part you need is a handful of commands, and they fit in an afternoon. This chapter is that part and nothing else: no branches, no servers, no merging. Those come in 0.4 and later, one at a time, when there is a reason.

Install it, and introduce yourself

Chapter 1 has the install steps per operating system: winget install Git.Git on Windows, the developer tools prompt on macOS, sudo apt install git on Linux. Check:

git --version
git version 2.45.2

Every snapshot you save carries your name and email, so git asks you for them once:

git config --global user.name "Ada Gopher"
git config --global user.email "ada@example.com"

Use the email you will sign up to GitHub with in the next chapter, so the two match. --global means “for every repository on this machine”.

On Windows, run git from PowerShell or from Git Bash, whichever terminal you have open; the commands are identical.

The idea: a commit is a snapshot

A repository is a folder git is watching. A commit is a snapshot of every file in that folder at one moment, plus a message, plus who and when. Commits form a chain, newest first. That chain is the history, and it never changes behind your back: a commit, once made, stays exactly as it was.

The one idea that makes everything else make sense: git does not save your changes automatically. You decide what goes into the next snapshot, and when. Saving a file in your editor changes the folder. Only git commit adds to the history.

Start a repository

Make a folder for a practice project and tell git to watch it:

mkdir notes
cd notes
git init -b main
Initialized empty Git repository in /Users/t/notes/.git/

-b main names the first line of history main, which is what GitHub expects. (An older git may not know -b; run git init and then git branch -M main.) The .git folder it created is git’s memory; you never edit it, and deleting it deletes the history.

The loop: status, add, commit

Ask git what it sees. You will type this command more than any other:

git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Create a file in the folder with your editor, todo.txt, with a few lines in it:

Things to learn
- the terminal
- git

Now:

git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	todo.txt

nothing added to commit but untracked files present (use "git add" to track)

Untracked means git sees a file it has never been told about. git add puts a file on the list for the next snapshot; programmers call that list the staging area, and “staged” means “on the list”:

git add todo.txt
git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   todo.txt

And take the snapshot, with a message that says what it is:

git commit -m "Start a to-do list"
[main (root-commit) 245e615] Start a to-do list
 1 file changed, 3 insertions(+)
 create mode 100644 todo.txt

245e615 is the first few characters of the commit’s id, a fingerprint of its contents. You will see these everywhere; you rarely type them.

That is the loop. Change files, git status to see what changed, git add what belongs in the next snapshot, git commit -m with a message. Everything else in this chapter is looking at history and fixing mistakes.

Seeing what changed

Add a line to todo.txt, - GitHub, and save. Before committing, see exactly what is different from the last snapshot:

git diff
diff --git a/todo.txt b/todo.txt
index f69bf85..4b52c0b 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,3 +1,4 @@
 Things to learn
 - the terminal
 - git
+- GitHub

Lines starting with + were added, lines starting with - were removed, the rest is context. git status --short is the one-line-per-file version, where M means modified:

git status --short
 M todo.txt

Commit it, then look at the history:

git add todo.txt
git commit -m "Add GitHub to the list"
git log --oneline
e6708f0 Add GitHub to the list
245e615 Start a to-do list

git log without --oneline shows author, date and the full message for each commit, in a pager: press q to get out. Newest is at the top.

A good commit message is a short sentence in the present tense saying what the change does, the way these two do. “Fix typo in README”, “Add the census project”. You are writing it for someone reading the history a year from now, and that someone is usually you.

Ignoring files

Some files should never go into history: passwords, logs, the output your program writes, your editor’s settings. Create a file with a password in it, secrets.env, and look:

git status --short
?? secrets.env

?? is untracked. git is waiting for you to add it, and one careless git add . would put the password into history forever. Tell git to ignore it instead, with a file named .gitignore containing one pattern per line:

secrets.env
*.log
git status --short
?? .gitignore

secrets.env is gone from the list, and *.log covers every log file you will ever create here. The .gitignore itself is committed, so the rule travels with the project:

git add .gitignore
git commit -m "Ignore secrets and logs"

The book’s own .gitignore is in the repository you will clone next chapter; it lists every output file the projects write, which is why running them never dirties your history.

Undoing mistakes

Three mistakes cover a beginner’s first year, and each has one command.

You changed a file and want the last snapshot back. Suppose you overwrite todo.txt with the single word oops. git diff --stat shows the damage, and git restore throws it away:

git diff --stat
 todo.txt | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
git restore todo.txt
cat todo.txt
Things to learn
- the terminal
- git
- GitHub

There is no undo for restore; it discards work you had not committed. Which is the argument for committing often.

You wrote the wrong commit message. Fix the most recent one:

git commit --amend -m "Add GitHub to the list of things to learn"
git log --oneline
70f7d46 Add GitHub to the list of things to learn
245e615 Start a to-do list

Notice the id changed: amending replaces the last commit with a new one. That is fine while the commit is only on your machine. Once it has been shared (0.4), you do not amend; you make a new commit.

You committed something that turned out to be wrong. Do not delete history. Make a new commit that undoes it:

git revert --no-edit HEAD
git log --oneline
436d3bb Revert "Ignore secrets and logs"
ce47be8 Ignore secrets and logs
70f7d46 Add GitHub to the list of things to learn
245e615 Start a to-do list

HEAD means “the latest commit”. The revert is itself a commit, so the history shows both the mistake and the correction, which is exactly what you want when someone asks what happened. (Revert the revert if you change your mind: git revert HEAD again.)

You will read about git reset and --force on the internet. Leave them until you know why you would want to rewrite history, because that is what they do, and the day you need them you will know.

The whole vocabulary

You want toCommand
start watching a foldergit init -b main
see what changedgit status, git diff
put a file on the list for the next snapshotgit add file (git add . for everything not ignored)
take the snapshotgit commit -m "What this change does"
read the historygit log --oneline (q leaves the pager)
discard uncommitted changes to a filegit restore file
fix the last commit’s messagegit commit --amend -m "Better message"
undo a commit, keeping historygit revert HEAD
never commit a fileadd its name to .gitignore

Conclusion

A repository is a watched folder, a commit is a snapshot you choose to take, and the daily loop is status, add, commit. You can see what changed, read the history, keep secrets out, and undo the three common mistakes without losing anything. Next: putting a repository on GitHub, getting the book’s code, and sending your first change to someone else’s project.

← Back to the blog