4 concepts of Versioning Control System every Software Engineer need to understand
The “commit” command is used to save your changes to the repository. Every set of changes implicitly creates a new, different version of your project. Therefore, every commit also marks a specific version. It’s a snapshot of your complete project at that certain point in time. The commit knows exactly how all of your files and directories looked and can be used to restore the project to that certain state.
Every commit item consists (amongst other things) following metadata:
- unique id/hash — every commit has a unique identifier.
- date — information when commit happened. It helps later on to lists the commits in chronological order
- author — information who performed changes
- message — the author of a commit has to comment what he did in a short “commit message”.
While in centralized version control systems like Subversion or CVS, an ascending revision number is used for this, this is simply not possible anymore in a distributed VCS like Git: The reason is that, in Git, multiple people can work in parallel, committing their work offline. That’s why Git is using a 40-character checksum called the “commit hash”
Commit only related changes!
When crafting a commit, it’s very important to only include changes that belong together. You should never mix up changes from multiple, different topics in a single commit. Commit should only wrap related changes: fixing two different bugs should produce (at the very least) two separate commits
Write Good Commit Messages
Time spent on crafting a good commit message is time spent well: it will make it easier to understand what was the intention when making these changes (and after some time also for yourself)
In every project, there are always multiple different contexts where work happens. Each feature, bugfix, experiment of your product can be seen as its own “topic”, clearly separated from other topics. In real-world projects, work always happens in multiple of these contexts in parallel. Not working in clearly separated contexts sooner or later will cause several problems:
- handle multiple topics in a single context would lead into a chaos (copying your complete project folder is not an solution)
- other team member can block you if they break something up
- providing a bug fix for customer without bringing new feature implementation which already started might be a challenging or not possible at all
- what do you do if that new functionality proves to be impossible to implement? It’s already mingled with all of those other changes, being almost impossible to separate out!
Branches are what we need to solve these problems. Because a branch represents exactly such a context in a project and helps you keep it separate from all other contexts. All the changes you make at any time will only apply to the currently active branch; all other branches are left untouched. This gives you the freedom to both work on different things in parallel and, above all, to experiment — because you can’t mess up! In case things go wrong you can always go back / undo / start fresh / switch contexts…
Use Branches Extensively
Branching is one of most powerful features. Branches are the perfect tool to help you avoid mixing up different lines of development. You should use branches extensively in your development workflows: for new features, bug fixes, experiments, ideas…
A tag represents a version of a particular branch at a moment in time (tag mark a specific commit in your repository history). Tags are commonly used to mark release versions, with the release name as the tag name (i.e. v1.0.1). A tag is like a branch that doesn’t change. Unlike branches, tags, after being created, have no further history of commits. When you tag a commit, you’re including all the changes before it. You can later compare tags to see the difference between two points in history.
Keeping your commits in the separate context of a branch is a huge help. But there will come a time when you want to integrate changes from one branch into another. For example when you finished developing a feature and want to integrate it into your “production” branch. Or maybe the other way around: you’re not yet finished working on your feature, but so many things have happened in the rest of the project in the meantime that you want to integrate these back into your feature branch. Such an integration is called “merging”
Integrating Branches — Not Individual Commits
When starting a merge, you don’t have to (and cannot) pick individual commits that shall be integrated. Instead, you tell Git which branch you want to integrate — and Git will figure out which commits you don’t have in your current working branch. Only these commits will then be integrated as a result.