Version Control
A version control system allows you to keep a history of your project. You can use it to revert back to an earlier version and see changes over time.
It is highly recommended to use a version control system even for smaller projects. In this guide Git is the recommended version control system to use. Git is be far the most popular version control system in use today. It is a distributed version control system, meaning each client has a copy of the entire repository.
Installation
To install Git run:
sudo dnf install git
Once installed you can use it either from the command-line or inside Visual Studio Code. Most IDEs come with an integration for Git, allowing you to use Git using a GUI from directly within the IDE. The Visual Studio Code integration with Git looks as follows:
Repository Strategy
When starting your project you need to decide what repository strategy to use. You can either store all of your project files in a single repository or multiple repositories.
Monorepo
The Monorepo strategy stores your entire project in a single repository. This is convenient and makes code reuse a little bit easier. Monorepo is the recommended strategy to use in this guide.
Polyrepo
The Polyrepo strategy stores your project in multiple repositories. Polyrepos can offer some advantages in larger projects in terms of scalability and more clearly defining which team owns what part of the project.
Repository Host
You have several options when it comes to where your git repository is hosted.
Local-hosted
A locally hosted git repository is one where there is no remote host. Your development machine is the only one holding a copy of your project. This approach is not recommended since you need to now manually handle backups of your project etc.
Self-hosted
Self-hosting a git repository on your own VPS or dedicated server is relatively straight-forward. It is also possible to self-host not only a Git repository but an entire DevOps software package such as GitLab.
Third-party Hosted
Letting a third-party host your git repository is the most convenient option. This is the recommended option in this guide. The Epic Fantasy Forge git repository is hosted on GitLab. GitLab is the recommended git repository host in this guide. GitLab is not just a git repository host but also a DevOps software package. It comes with an integrated CI/CD system.
GitLab is offered in three tiers:
- Free
- Premium
- Ultimate
The tier recommended in this guide is Premium. It costs $348 per year. As in this guide we will be creating macOS and iOS software, we will need to use a macOS GitLab runner. The macOS GitLab runner is only available in the Premium and Ultimate tiers.
Furthermore the free version of GitLab only provides 400 CI/CD minuters per month, which may not be enough if you regularly work on your project. With the Premium plan you get 10,000 CI/CD minutes per month.
Creating a Git Repository
To create a git repository in GitLab, create an account on GitLab. As recommended in this guide, subscribe to GitLab Premium. Once you have an account you can optionally enable dark mode by clicking on your profile icon and selecting "Preferences":
Select "Preferences" and choose "Dark (Experiment)" in the "Appearance" section.
To create a git repository, go to your "Projects" page and click "New project":
Choose "Create blank project":
Set a "Project name", select the appropriate project visibility level and check the checkbox "Initialize repository with a README" and click "Create project". For open source projects select "Public" for the project visibility. For closed source projects select "Private" for the project visility.
You now have a a git repository hosted by GitLab:
SSH Keys
To commit to your git repository you need to somehow authenticate yourself. This is either done via public-key authentication using SSH or a password or token using HTTPS. In this guide it is recommended to use SSH.
SSH Key Creation
You may or may not already have an SSH key pair on your development machine. To check run the below command in your terminal:
ls -la ~/.ssh
If you already have an SSH key pair then you should have at least two files in your .ssh directory, usually named id_rsa and id_rsa.pub by default. The former is your private key and the latter your public key.
If the command above outputted an error along the lines of "No such file or directory", or your .ssh directory is empty then you can easily generate an SSH keypair by running the following command:
ssh-keygen -t rsa
You will be prompted several times. You can just hit the "Enter" key on each prompt. You now have an SSH key pair.
Warning
Your private key, named id_rsa by default, should not be shared with anyone. If someone is able to get access to your private key they can impersonate you and access all services using your account where you use public key authentication.
Warning
When generating a new SSH keypair when you already have a keypair, make sure to only overwrite an existing keypair if you really don't need the old keypair anymore. Otherwise you may lose access to services where you previously used the old keypair for public key authentication.
Adding your Public SSH Key to GitLab
To add your SSH key to GitLab, click on your profile icon and select "Preferences":
Then select "SSH Keys" and click "Add new key":
For the next part we will need your public SSH key. You can reveal your public SSH key by running the below command assuming your public key is located at ~/.ssh/id_rsa.pub which it should be by default:
cat ~/.ssh/id_rsa.pub
Copy & paste the output of the above command and paste it into the "Key" section on the next screen. Set the "Usage type" to "Authentication & Signing" and click "Add key". Optionally you can also set a custom title for your key and an expiration date. By default the expiration date is set to 1 year in the future.
You have now added your public key to GitLab. You can now use your SSH keypair to authenticate with GitLab.
Cloning a Git Repository
You can now use your newly created git repository by cloning it in Visual Studio Code. For this we will need your repository URL which you can get by going to your git repository's main page in GitLab, clicking on the "Code" dropdown menu and clicking the clipboard icon beside the "Clone with SSH" entry:
Now open Visual Studio Code, select the "Source Control" tab, open "More actions..." and select "Clone":
Paste the URL you copied earlier into the dialog and click "Clone from URL":
Choose a location where to clone your git repository into and click "OK":
If Visual Studio Code asks you how to open the new repository choose "Open":
If Visual Studio Code asks you whether you trust the authors choose "Yes, I trust the authors":
You can now work on the repository using Visual Studio Code.
Git Configuration
When committing to your git repository, the git commit log will list you as an author with the configured name and email address. To configure your name and email address run the below command to edit your .gitconfig file using nvim:
nvim ~/.gitconfig
You can now populate your .gitconfig with your name and email address. For example my .gitconfig file looks like this:
[user]
name = Henrik Brüsecke
email = [email protected]
Committing & Pushing
You can now make changes to your git repository. For example, let's clear out the auto-generated README.md file in your repository:
Select the "Source Control" tab on the left sidebar, fill in a commit message e.g. "Clear README", open the accordion beside the "Commit" button and select "Commit & Push":
You should now be able to see your pushed commit in the GitLab web UI. To see it go to "Code" and select "Commits":
Your last commit "Clear README" is now visible in the GitLab web UI:
Merge Strategy
In our last commit we pushed straight to the main branch. This is not necessarily an ideal practice. Without some kind of commit strategy our main branch can become polluted with small meaningless commits. Personally I prefer to work on a development branch and then use a merge request to merge the development branch to master. During merging all the smaller commits in the development branch will be squashed into a single larger commit on the main branch.
Protected Branch
To protect the main branch, select "Settings" and choose "Repository:
Now open the "Protected branches" accordion. By default the main branch should already be protected but it allows direct pushes by maintainers. Set the "Allowed to merge" field to "Maintainers" and "Allowed to push and merge" field to "No one":
Branching
Since we are no longer allowed to push directly to the main branch we must now work in branches. To create a new branch in Visual Studio Code select the "Source Control" tab, click the three dots for "More Actions", choose "Branch" and select "Create Branch...":
Enter a name for the new branch, e.g. "demo-branch", and press "Enter":
Click the "Publish Branch" button so it is created on your remote repository (GitLab) also:
Now make a change to the README.md file, e.g. enter some text and save the change. Then enter a commit message and from the drop-down menu beside the "Commit" button select "Commit & Push":
When prompted about unstaged changes press "Yes":
Merging
By default, when merging a merge request in GitLab an ugly merge commit is created in the target branch (usually main). To avoid this and generally harden our merge settings we need to navigate to the "Settings" and select "Merge requests":
On this page, configure the following:
- Select "Fast-forward merge" for the "Merge method".
- This prevents an ugly merge commit from being created. From the main branch's point of view it will look like the merge request was directly committed to the main branch rather than merged. This keeps the main branch's commit log clean.
- Select the "Enable Delete source branch option by default".
- This keeps our repository clean by automatically deleting development branches as they are merged to the main branch.
- Select "Require" for the section "Squash commits when merging".
- This prevents very small development changes, appropriate for a development branch but inappropriate for a main branch, from polluting the main branch commit log.
- Later in this guide we will select the "Pipelines must succeed" checkbox in the "Merge checks" section.
- This prevents broken code from being merged to the main branch.
- At this point in the guide we don't have a pipeline yet but we will create one in a later section of this guide.
- For now leave this checkbox unticked
Now click the "Save changes" button.
Merge Requests
Since the main branch now no longer accepts direct pushes we must create a merge request to merge changes to it. In my opinion merge requests are useful even to solo developers. They allow you to review your changes a final time before merging to the main branch. Additionally they can be used to enforce clean practices such as squashing commits and enforcing fast-forward merges to keep the commit log of the main branch clean.
To create a merge request in GitLab, select "Code" and click on "Merge requests":
If you have recently pushed to a development branch, GitLab should automatically prompt you if you would like to create a merge request from this branch. Click on "Create merge request":
Optionally change the title and click on "Create merge request":
You now have an open merge request. Review the changes under the "Changes" tab. Once happy with the changes click the "Approve" button and optionally change the "Squash commit message". This is the commit message that will appear in the main branch commit log after merging this merge request. Now click the "Merge" button.
The merge request is now merged.
The commit log in the main branch shows a single commit that looks like a regular commit. There is no ugly merge commit.
Workflow
When implementing a change, the Git workflow recommended by this guide is:
- Checkout the main branch
- Pull the latest changes
- Create a new development branch
- Implement your changes in the development branch in small granular commits
- Create a merge request once your changes are ready
- Merge the merge request using the fast-forward merge strategy and squashing the commits
Open Source vs Closed Source
A project can be open source or closed source. Open source means anyone from the general public can see the source code and source files of the project. Closed source means no one except authorized users can see the source code and source files of the project.
In this guide it is recommended to try and make your project open source if possible. This makes it transparent to users how your project works. It can also be useful for learning purposes.
Warning
If your project is open source, special attention must be paid to not accidentally leak secrets to the general public, such as API keys or passwords. It is recommended to store such sensitive data in GitLab variables which can be hidden from the general public. These variables can then be injected as environment variables into your infrastructure. Your infrastructure related scripts can then remain open source since they don't need to contain any secrets. They can read the secrets from environment variables at runtime.
Binary files
Generally speaking, any files generated during the build process of your project should not be committed to the repository. This includes the artifacts of your build, e.g. binary executables and also temporary build files.
Howver, in my opinion, it is OK to store binary files in the repository that are not generated during the build process and that are not expected to change very often. This includes assets such as images. Storing binary files such as images outside of the repository is not worth the hassle, it increases complexity and also requires its own backup mechanism.
Git Ignore
As mentioned above in the section Binary files, you should not add temporary build files and other files generated during the build process to your Git repository. To prevent accidentally adding them in future you can create a file named ".gitignore" in the root of your Git repository. In this file you can add files and/or directories which should not be added to the Git repository. So far we don't have a build yet but we will add one in later sections of this guide.