A git primer for future colleagues
You become a git expert when you learn about git reflog. Not because it’s an advanced feature.
But because it gives you the confidence that whatever command you run can be reverted.
Once you can undo any git command, your willingness to experiment increases significantly, and you become the export.
Besides that, git reflog is not the most useful command, since it’s mostly for when you fucked up,
which is less and less.
Here’s a list of more useful low-risk, high-yield commands you should know about.
Table of contents ¶
git commit --amend
¶
You made a commit and submitted a pull request, and you found out you need to make some changes.
Make a few changes, commit them, and now you suddenly have a less readable commit history.
That second commit was a part of the review process and does not serve future readers.
commit b3f9a21 (HEAD -> feature-login)
Author: Simon Shine <simon@simonshine.dk>
Date: Fri Feb 14 10:45:23 2026 +0100
Fix typo in error message
commit a7c8d42
Author: Simon Shine <simon@simonshine.dk>
Date: Fri Feb 14 09:30:15 2026 +0100
Add user login functionality
Instead, you can git add your changes and git commit --amend.
This adds your changes to the most recent commit (HEAD) instead of its own.
One rule: You can only amend commits that aren’t shared with others yet.
git add -p
¶
You made a lot of changes, and now you’re ready to commit them. There was some trial-and-error, and you also made some unnecessary changes; failed attempts, random edits here and there. Don’t include them in your git commit. But undoing them is tedious.
Instead, git add -p progressively, meaning repeatedly, asks if you want to add another change.
$ git add -p
diff --git a/src/auth.js b/src/auth.js
index 1a2b3c4..5d6e7f8 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -10,7 +10,7 @@ function validatePassword(password) {
- return password.length >= 8;
+ return password.length >= 12;
}
Stage this hunk [y,n,q,a,d,s,e,?]? y
diff --git a/src/auth.js b/src/auth.js
@@ -25,6 +25,7 @@ function login(username, password) {
+ console.log("DEBUG: login called");
if (validatePassword(password)) {
Stage this hunk [y,n,q,a,d,s,e,?]? n
diff --git a/package-lock.json b/package-lock.json
@@ -1234,5 +1234,5 @@
- "version": "1.0.0",
+ "version": "1.0.1",
Stage this hunk [y,n,q,a,d,s,e,?]? n
This gives you a chance to review your changes before committing and submitting a pull request.
For example, were those debug prints or the changes to your package lock really necessary?
git commit -v
¶
--verbose or just -v makes all the changes appear in the commit message editor.
To write a good commit message, having the content of your commit in your editor while composing the message is valuable. Nowadays you might prefer for an AI to write your commit message. But I still find plenty of occasions to run Vim (Neovim) in split window mode, one window focusing on composing, and the other on what’s being committed:

git rebase
¶
Rebasing means replaying your commits onto another branch.
Let’s say you create a feature branch when ‘main’ is at commit E, and by the time you’re done, others have merged things so that the HEAD commit of ‘main’ is now G. Your feature branch is ready to merge, but between E and G, there might be changes overlapping with those on your feature branch.
Before rebase:
A---B---C feature
/
D---E---F---G main
After rebasing 'feature' onto 'main':
A---B---C feature
/
D---E---F---G main
You want to make git rebase a core part of your daily workflow. For example, in the morning before
you resume work on your feature branch, sync your main and your feature branch:
- Stash any uncommitted changes (using
git stashor a WIP commit) - So that you can
git switch mainto switch to your main branch - Fetch any changes on
mainusinggit pull --rebase --prune
This updates your local main so that it’s in sync with the remote main. You naturally don’t have
any commits on your local main since you’re submitting pull requests on feature branches for that.
But if you happened to have changes, those would get replayed on top, avoiding merge commits on
main that don’t exist remotely.