WARNING: ranting incoming.
I have seen countless articles about git (one of the hottest subjects out here), yet I am still puzzled at how few developers actually took the time to learn git.
You do not know git
To clarify what I mean by that, let's play a game. If I ask you to create a feature branch, commit some changes, and make a pull request, I am sure everything is fine. But now, let me vary the game:
level one: you can't use any external tool such as Gitlab or your favorite IDE. No clickops, terminal only. This holds for all the other levels.
level two: you have to split your changes into 5 commits. Bonus: the files are from only two files. And please, don't use the old trick of copying all the changes somewhere, and then pasting them one by one to "prepare" each commit.
level three: you pushed your commits 1 to 5, but your reviewer asks to change something in commit 2, reorder commits 3-4, meld commit 5 into 4, change the commit message of commit 1, and rebase your branch to master.
level four: you made a mistake during the level three operation. You need to "recover" and start again, but you already pushed your changes, so a force pull from the origin won't work.
bonus: a bug was introduced into the codebase, but you have no idea when. How do you use git to find the commit that started it all?
All of this is doable when you understand the concepts and inner workings of git - (I wanted to write "when you start actually giving a f**k about the tool you use every day").
Why bother? Well, the operations I outlined in the game can:
speed up your development process,
make your codebase cleaner and your reviews easier: if all the developers in your team learn how to properly split commits and organize them, you can start thinking about conventional commits, automated changelogs, atomic commits, continuous deployment, clean review processes, etc.
make you fit for open-source: yup, you'll need all that if you want one day to contribute to the Linux kernel or other important codebases!
save your life when inevitably someone (you?) screws up a git repo
[... ask ChatGPT to fill up the list ...]
Don't get me wrong. I am not saying everyone should be a complete master of git, but when 99% of the codebases worldwide are managed through git and "gitting" is a daily activity, I believe spending some time understanding the power of it may be worth it. I myself do not know all the git commands by heart, but I know enough to be aware of the possibilities (and what to google for).
But you can start learning
Now that I (I hope!) convinced you, here are some pointers on how to start.
Start with the basics
Start by learning git the hard way. Stop using your IDE or git UI for a while, until you feel at ease with all the basic operations directly using the command line. Do not learn by heart, but ask yourself what git is doing under the hood. For example, "staging area", "branches" and "remote" should be crystal clear. You should also understand what's inside the .git/config
, how .gitignore
works, and what are refs.
Here are some of what I consider "basic" commands:
the obvious
clone
,pull
,push
,fetch
remote management:
git remote add|remove|show origin
git status
, and how to add(remove) files/folders to the staging area (e.g.git rm --cached -r foo/
)commit (with short and long messages), and especially amend the last commit (e.g.
git commit --amend --reset-author
). Also, ensure you know how to revert the latest commit(s) (git reset --hard/--soft
).basic branch and tag management:
git checkout [-b] ...
,git branch ...
,git tag ...
. Note that checkout works not only with branches but also with files!stashing (please, do it for yourself!):
git stash
/git stash pop
, ...merge and basic rebase:
git merge
,git rebase
understand
git log
, (or better,git log --graph --format=oneline
) and view a specific ref (git show
,git revparse
)some other handy commands:
git diff
,git revparse
,...
Note: this is a very generic advice. When I started programming, my first Java class was not about primitives, it was about java
and javac
. I started with a Notepad and a terminal, and only 4 weeks into the class was I allowed to use an IDE. This was brilliant and stayed with me ever since.
Get comfortable with rebasing
git rebase
is so powerful it is blinding. You could spend months just playing with it. I personally only know 2 things by heart: (a) how to rebase a branch onto another (e.g. git rebase master
from a feature branch) and (b) how to do interactive rebase.
Play with interactive rebase yourself, and get familiar with the options: pick
, reword
, edit
, squash
, fixup
. Learn how to handle conflicts during rebase, and how to abort.
If you fear you'll mess up, don't forget nothing is undoable in git. You can stop an interactive rebase at any point using the git rebase --abort
command. If you already finished the rebase, you still have the reflog ;).
Experiment and learn how to undo anything
Trying things out is difficult when you fear about messing up. But anything you do on your local repository can be rolled back. You may have already learned about git reset
and git pull --force
, so the next step is to get acquainted with the reflog.
Reference logs (reflogs), keep the history of when the tips of branches and other references in the local repository are modified. For example, when you create/delete a branch, do a rebase, merge something, etc. Each log is associated with an SHA, that you can use to go back in time exactly as you do with commits, using git reset <reflog SHA>
.
I'll let you figure out the rest by yourself! But now that you are aware of its existence, there is no excuse to try things: merge, rebase, delete, mess up all you want, you're covered.
Be curious
When you return to using your IDE / git GUI (tip: try lazygit, it's AMAZING), always ask yourself what command(s) are used in the background.
Do a git --help
once in a while, and read about the commands you never encountered. For example, have a look at git blame
, git bissect
, etc.
Ask your peers how they use git, and learn from them. Share your learnings and spread the knowledge.
Up your game
Now that you're familiar with git, it is time to step up your game. Think of how it could help you streamline your processes and how it can benefit you and your team.
As a bonus, here are additional advice:
Sign your commits using GPG keys (this will annotate your commits with a nice "verified" badge on GitHub)
Improve your pull requests by following the conventional commits convention, and your reviews by following the conventional comments.
Keep a clean git history using squashing, and think about adopting atomic commits
Top comments (16)
A big applause/thanks to @jacks who took the challenge seriously and shared his experience and learnings. If you are looking for answers, check it out!
Challenge: Trying to GIT GUD
jacks50 ใป Aug 12
Your game sounds scarily familiar! I liked your article, and definitely agree people should learn git, especially beyond the bare minimum steps to push/pull and open a PR.
I'm just as comfortable in git's CLI client rebasing interactively to edit the commits, reorder them, or squash/fixup them together. I can also use the reflog to full effect, although this is where I prefer IDE tools which nicely graph the reflog entries and dig into each for quick analysis. But the visual graphing isn't super helpful if someone doesn't even know what the reflog is.
I also suggest anyone looking to learn more about git check out the Oh My Git! game. It teaches git in the premise of it being a time machine and has visual and CLI interface, so someone can definitely learn the CLI steps and work towards mastering git. The game is in GitHub and you can even create new levels for the game and submit them as Pull Requests.
Awesome, thank you for sharing Oh My Git!, looks very cool!
Thank you for this writeup. It makes me feel like I'm not crazy for wanting to request these things during code reviews.
I'm able to do all these things but some are faster with lazygit or git-gui. Is there a good way to split a commit with just commandline? I can think of resetting and just git add -p to stage the partial changes.
(my own rant coming) I especially want to tell one contributor to squash all his changes (he makes small nonsense commits and git merges from main so rebasing is not possible) into one and then split that into logical commits where he removes or splits off unrelated changes into a separate branch if he wants to keep them for another PR. But even before settling on the final code, there's the half a dozen changes that need to happen for the code to work as required. Getting to the working code is a challenge, then getting to the minimal code is another one. By the time that's done, It's been 2+ months and I just squash all the changes rather than wait for him to make it nice for the merging.
I literally struggle with GUIs. People at work just sigh when I have to use them because they know what's coming. The command-line makes sense to me, looking at pictures of things and remembering where menus are and what icons are supposed to represent gives me anxiety.
Something like SourceTree is hard mode!
I feel you!
I am curious, do you consider lazygit and the likes as GUI tools or terminal then?
I've tried to use them and I see how they can be a good thing, but I get lost and go back to the command line. I never got on with file explorers like mc or ranger either :)
Loosely related: the Linux kernel development process is quite an interesting read!
learn git branching is a web based game about learning git and the fundamentals. It's good for learning the basics of git
what kind of a madman person someone would have to be to ask the things in level 3? :D
goot post!
The only one I don't think I've been asked is to simply reorder the commits. Although I guess if 2 different features were in a single branch, I could see wanting all commits for feature 1 to come first and then feature 2 after, if they were completely independent of eachother. It could make testing feature 1 easier to checkout the last commit prior to feature 2.
However, I'd have to argue that should have been 2 separate PR's anyway, feature 2 could have been based off the feature 1 branch if they are related. A reviewer with that much OCD but not asking for separate the PR's, at least in my experience, is not extremely common. Although I've seen when 20-30 tickets end up in a single feature branch tied back to a single epic (jira terminology), so its definitely not unlikely to happen either...
I agree the reorder alone doesn't really make sense. However, you may need this skill of e.g. Commit 2 and 4 should be squashed. In this case, you can first reorder then squash. I see other use cases for the reorder (locally when you start fixing one commit multiple times, you can bring it at the top and then amend, amend, amend).
The game is really just a game to sustain my arguments, I hope in real life your reviewers have more sense ๐
I agree with your answers, thanks for sharing!
Yep, and of course the interactive rebase and just moving the pick'd commit up and down solves the trick. I've done this myself often enough for my own nitpicky reasons. ๐
Also just like you say to work on something without changing a commit history deeply over and over moving it to the top then fixup/squash together, amend/reword the messages, and then reorder it back into place before opening a PR. Which of course if anyone likes their PR to look clean, its nice to do as much of that as possible prior to opening a PR! All the force pushes to fix the origin branch after a PR is opened look kind of messy when its not based on review comments. ๐
I would like to avoid naming some of my colleagues ๐. Thanks!
While I can split a commit into n smaller commits, needing to do so would mean I had done something wrong. (Probably means I should have done refactoring long before.) The changes in a commit need to be big enough to build and test with no unexpected* test failures.
Where I work, our most important rule for commits is "Test then commit." Any unexpected* test failures must be either resolved or justified before doing the commit. As such, we don't use the stage as this risks committing code that differs from what was tested.
As a team leader, I require my teammates to either properly use rebase or to not use rebase. By properly, I mean each revision created is tested and fixed/justified before being committed.
Far too often, I encounter strings of untestable commits resulting from lazy use of rebase. I would much rather have a testable merge-commit (preceded by unrebased commits) than a series of untestable commits.
Untestable commits make bisection so very much harder to do.
*Unexpected test failures occur when either the changes don't (completely) fix the intended issue or are new issues either uncovered or caused by the changes. Expected failures are from issues that are known but not yet addressed by code changes.
It's crazy how fiery posts get so much traction on blog sites.