Sigmund Freud once said “Dogs love their friends and bite their enemies, quite unlike people, who are incapable of pure love and always have to mix love and hate.”
Now, I’m no expert in psychiatry. Nor do I claim to be. But when it comes to my relationship with version control I would definitely say that this is true. However, after having worked with it quite a bit I would say that the benefits outweigh the risks. Having the ability to go back to a previous point in the production or the ability to have developers work on different features at the same time are just a few things that I have come to love with version control.
But as it also brings a bunch of difficulties I thought I’d talk about what I’ve done and, more importantly, what I have learned when working with version control. This is the first part of a series of posts where I will do my best to explain version control. The ultimate goal of these posts will be to help you to start using version control for your game projects. Obviously you’ll be able to use it for other projects as well, but I will cover some topics that are particularly useful when making a game.
It should be noted that I am by no means the Mr. Miyagi of version control (yet). So if you have worked with it a lot already you can most likely stop reading at this point. But hopefully I can at least help someone avoid a few pitfalls.
The version control system that I, and I’d say most people at this point, have chosen to work with is Git. So that is what these posts will cover. While most of what is discussed in this post applies to all version control systems, some parts are unique to Git.
What is Git
You might be thinking “Alright, what you’re say sounds cool and all. But what is Git, and how does it even work? And why should I use it?”
Short answer: Its complicated
Long answer: Its complicated, and I don’t really have the knowledge (or time) to explain how it works in minute detail.
But. You need not fear my young aspiring Git user. I will do my best to explain the general concept, which should be enough to get you up and running.
I like to imagine Git as a magical tree, with the memory of an elephant. This tree does not need water and sunlight to grow. The way it grows is by “commits”. Commits are save points that Git tracks. They allow you to return to this specific point in the project history at any time. This is where the elephant memory comes in. Git remembers everything. For the sake of this analogy commits are the leaves on our tree. You can create commits at any time. One does this by first “staging” the files that are to be included in the commit (in most cases this is all files you have been working on since your last commit). When you commit your chunk of files get added to the top of our magical tree.
Doing this will leave us with a pole like thing with a bunch of leaves on it. That doesn’t sound much like a magical tree. The thing that is missing is of course branches. “Branches” are another great feature of Git. Every Git repository (repo for short), which is the actual name for an instance of our magical tree, starts with one branch. This branch is referred to as the”trunk/master branch” and its the backbone of our repo. To create a branch we simply select one of our commits (this can be done on any commit at any point in time) and tell Git to create a branch there. A copy of our project at that commit is created in the same place on the tree. We can then start growing it with commits, just as previously explained.
Its important to note that any commits we do now will only be added to the current branch. To switch between branches we need to “checkout” the branch we want to work with. Checking out a branch puts us at the latest commit in that branch. We can checkout a branch at any point, as long as we don’t have uncommitted changes on the branch we are currently on (if you want to switch branches without committing your current changes I’d recommend looking into the stash/pop function of Git). When we checkout another branch Git replaces all the files in your project folder with those of the other branch. It still keeps track of your other branches though. So there’s no need to worry about losing your work.
The cool thing with branches is that it gives you a safety net when adding new features to your project. All you do is create a new branch before starting to work on a new feature. If you don’t like the feature once you are done with it you can simply just abandon the branch and checkout another one.
“But what if I like the feature on the branch?” you say. Well, this is where the shape of our tree gets a little weird. The magical Git tree has the ability to “merge” branches into one another. When doing a merge Git will do its very best to automatically put together the content on one branch with that on the other one. Most of the time it does quite a good job. Sometimes, not so much.
If Git can’t manage the merge itself you will be faced with a merge conflict. You will then need to manually go in and tell Git what parts of what files to keep from the different branches. This can be kinda difficult at first, but whit time it gets easier. I unfortunately can’t really give you an all-encompassing solution to how to resolve your merge conflicts, as they tend to be different on a case-to-case basis. If they weren’t Git would probably already solve them. What I recommend it that you just make sure to back-up your files in another location and then just do your best to solve the conflict. Worst case scenario you just need to undo the merge and revert to your backed up files.
Making sure you have a proper workflow when using Git is a good way to minimize the amount of times that a merge conflict happens. A non-existent merge conflict is the easiest merge conflict! Having a good workflow also helps when making a game in Unity (which is what my team’s current game is being made in), as Unity tends to be less than helpful when it comes to co-existing with Git. But the info about proper using a proper workflow will have to wait until the next post. We still have some basics to cover.
So now we have our nice little tree. And if you are working alone on a project this could be the only things you’d need. However, more often than not you will be working with others. For this there’s a few more things you need to know about. Specifically pushing and pulling.
The standard setup when doing collaborative work in Git is to host the repo on a server and then anyone contributing to the project simply syncs up with that repo. Git handles this in quite a smart way that allows contributors to keep working on a project, and even switch between branches, without always being connected to the server. Our magical tree is placed on the server. This is often referred to as the origin tree. Then when people want to contribute to the project they “clone” it. This creates a local copy of the tree, a sapling if you will, on the contributors computer.
Side note: A lot of times people use Git and GitHub interchangeably. This is incorrect. Git is, as you should know by now, a version control system. GitHub on the other hand is a site that is quite often used to host Git repos. I will talk about this more in later posts, but I just wanted to note this to make sure you don’t make this mistake.
Lets say that we are working with a friend, I’m gonna call him Henry. We have both cloned the repo from the server. As we work and make commits our tree will grow. This does however not automatically sync to the server or Henry’s repo. At first this might seem annoying, but many times this can stop bugs from being synced over to everyone working on the project.
When we are happy with our current work and we want to sync it to the server what we do is a push. This updates the origin repo. Now Henry’s repo is out of date. To get the latest changes Henry needs to make a pull. Which updates his repo to be synced with the origin repo.
Finally I just want to note two things about pushing and pulling:
1. Pushing and pulling is done on a per branch basis.
2. This is very important. ALWAYS PULL BEFORE YOU PUSH! if you don’t do this you might completely mess up the origin repo.
And those are the basics. You are now well on your way to become a master arborist :D
I know this post didn’t go too much into specific detail on how to actually work with git. But I wanted to make sure that you had an understanding of how Git actually works before just telling you how to use it. In the next post of this series I will cover how to get setup.
If you feel like I missed anything, or if you have any further questions. Feel free to leave a comment, or use any of my other contact methods, and I’ll do my best to answer your question(s).