Over the past few months I have been using Git & GitHub more frequently, both in my professional and personal work, with this came many questions about what the “correct” way is to use Git.
There are obviously many ways to create workflows using Git, however below is the way that I have started to manage my workflow, this is likely to change over time as it is only my first workflow but this is a start!
What to solve?
There are many things that I didnt like about the way I used Git in the past and so these are some of the issues I am aiming to solve:
- Standardised git commit messages
- How best to utilise Branches
- When should Pull Requests be used
- How can the workflow be Automated
Why solve them?
Well this is quite straight forward, to improve the readability of my Git Repos especially in open source projects, but also to keep my mind clear and organised.
How were these issues solved?
Below I have split each area to solve out, this explains how I solved the issues I was experiencing.
Versioning was something that I never thought about, I increased when I wanted to based on what I thought was right.
Then I started doing code professionally and was introduced to the Semantic Versioning specification.
This made much more sense by adding a relationship between each different increment.
A version number would be MAJOR.MINOR.PATCH, Increments as below:
MAJORversion when changes are mede that would break previous functionality.
MINORversion when functionality is added in a backwards compatible manner.
PATCHversion where you make backwards compatible bug fixes.
by using this method people are now able to easily identify what type of change has been implemented and if it is likely to break their current project.
My commit records were… well… a total mess, Looking at other repos this is quite common and not many projects follow a standard. I was looking for a better way to provide commit messages that just make sense and are easy to read, in my research I found a standard called Conventional Commits.
Conventional Commits is a specification for adding human and machine readable meanings to commit messages, this then allows the creation of ChangeLogs through Automation and makes life easier for a human to tell what has changed!
The specification is real simple so doesn’t take much to get your head around:
<type>[optional scope]: <description> [optional body] [optional footer]
The commit contains the following structural elements:
- fix: a commit of the type
fixresolves a bug in the codebase (
- feat: a commit of the type
featadds a new feature to the codebase (
- Breaking Changes: a commit that appends
!after the type/scope, where a breaking change is introduced (
MAJORin SemVer). A breaking change can be part of commits of any type.
- Other types are allowed for example:
- Body & Footers may be provided to include Breaking Change as well as other information.
fix(core): error handling of CLI Command
This example shows a
fix for the
core scope and the fix was for error handling of CLI Command
feat(core)!: Updated API to add more functionality to xxx
This example adds a
feat to the
core scope, its a breaking change and shows that more functionality was added to the API.
As you can see this is such a simple and easy to understand convention which is very easy to use in automation if needed.
When I first started using Git Branches were something of a mystery to me, I didnt understand why you would want to commit to a branch and then have to push to master. After using Git for a few years I have realised their huge benefits and how they assist me with automation of tasks.
For every issue that is reported I will create a new branch, the branch will be named as follows:
By doing this I am able to quickly link a branch to a specific issue in the project. Branches also enable me to make multiple commits at smaller increments, which I then use Pull Requests to merge with Master
I now utilise Pull Requests to move my branch into the master, the pull request has various checks using GitHub Actions depending on the project type This would be things like:
- Version check: confirm that the version in the project files has been incremented since the last release
- Tests: Check that the code functions as expected
- Linting: Check that the code still adhears to the relevant standards
With all of my Repos I will only enable
Allow squash merging this allows me to create one good commit message that covers the issues fixed for the specifc branch we are merging, rather than all the commits from the development lifecycle (keeping my master commits clean)
Once I have completed all of the pull requests for a specific release I will then add a version
tag to the master.
This version tag creates a point in time referance along with triggering my release automation once it is pushed.
In order to streamline my delivery to release I have started to utilise GitHub Actions, This allows me to have endless automation capabilities.
Currently I utilise Actions for the following:
- Version Checks
- ChangeLog Generation
- Release creation
- Push to external artifactories (e.g. Docker Hub, Ansible Galaxy etc.)
The changelog and release process is something that I have just started doing, I was manually writing out my changelog for any new releases which was time consuming and required a lot of manual back and forth to confirm what was changed, not an issue whilst a project is small, but if it grows that would quickly become out of control.
I believe that at this time for the work I am doing this is the best workflow for myself, If you have any thoughts on ways this could be further improved, please let me know over on my Discord