When I switched jobs four years ago, I went from using subversion (svn) to using git as the version control system. Even though I am a pretty quick learner, it took me a quite a while to really understand git. I read a lot on how git works, but even so, I didn’t always realize what the implications were for how to use git. Here are six big “aha moments” I had on how to use git.
Looking at these aha moments now, they all feel really obvious. But I remember literally thinking “aha” when I understood a concept and realized the ways in which I could then use git.
1. Local and remote changes are independent. I often used “svn status -u” to see what changes I would get if I ran svn update. In the beginning I was confused about the equivalent operation in git. You can run git status, and that will show the local changes compared to what was last pulled. But to see the changes on the server, I first need to do a git fetch. This concept of there being two independent sets of changes – local changes, and changes on the server (pushed there by other people) – took me a while to really grok.
2. Merge makes older commits accessible. It took me a long time to realize that merging simply makes already existing commits accessible. Initially my mental model of a merge was that all changes from the merged branch became commits at the time of the merge. Realizing that commits from a branch remain, and that merging simply allows them to “be seen” in the other branch helped explain some odd things. For example, showing git log of a file (for example in PyCharm) can show commits from two different branches interleaved. Showing a diff between two commits (adjacent in time, but on two different branches) can give a really big diff.
3. The meaning of merge commits. Since my understanding of merging was incomplete, merge commits used to be a bit of a mystery to me. I viewed commits only as “what is the diff of the code for this commit”, and from that perspective, merge commits didn’t make any sense. Often they are empty, with no code diff at all. Now of course I understand that it is important to see at what point commits from another branch were made reachable by a merge.
4. You can merge in both directions. If you have a long-running branch b that you eventually want to merge back into master, you can resolve the merge conflicts a bit at a time by periodically merging master into b (and fix any merge conflicts that arise). Eventually I want to merge all the changes from b into master, but that can only be done when b works as intended. It was a big conceptual aha moment for me when I realized that I don’t have to wait till b is done before I resolve some merge conflicts. Merging master into b at regular intervals means that when I finally merge b into master, most of the merge conflicts will already have been dealt with.
5. You can start over if a merge fails. When I used svn, I often used –dry-run to see what would happen if I did a certain operation (but without actually doing it). In git, I took me a while to realize that the same functionality exists, but it is not defined explicitly. For example, if I merge some changes, and I mess up resolving the merge conflicts, I can simply go back one step and do it again. As long as I did not push to the server, it is no problem to go back by doing a “git reset –hard HEAD~”. Now I can simply do the merge again, and hopefully resolve the conflicts correctly this time. It is very liberating to know that almost anything I do with git can be easily reverted, undone or redone.
6. Committed changes can be reset to local changes again. Sometimes I work on a branch b with local changes, and I need to check out another branch and do some work there. In the past, I would often stash the local changes. But these days I often commit the local changes, with a commit message that it is work in progress (WIP). I don’t push these changes to the server. When I come back to branch b, I do a “git reset HEAD~” to get the committed changes back as local changes again. Making WIP commits on a branch means I don’t have to keep track of which branch the stashed changes are meant for. It was a real aha moment for me to realize that I can “undo” the commit into local changes again.
I remember reading a lot of tutorials on how to use git, and how git is organized. But even knowing how git works didn’t immediately translate into understanding how I could use git. The above aha moments only depend on understanding a few things – that there are local changes, and how branches and merging works. Knowing what I know now, all these insights seem trivial. However, I remember quite clearly that these were not obvious to me when I started learning git. Hopefully these aha moments can be useful to somebody else new to git.