The Git Versioning system
You are likely already familiar with Git. In Braiins, we take great care to use Git properly, with the aim of creating a clear, consistent and linear history.
Clients
The lingua franca of Git is the default command-line client, the git command.
Make sure to be familiar with it, as most materials will only refer to it instead
of pulling apart each GUI client. You can expect that your colleagues and reviewers
of your merge requests will advise/request changes in terms of the Git cli also.
If you want to use a GUI/TUI client for git, make sure said client gives you a great degree of control, is unopinionated and does not insert any fluff or garbage config files into the repository.
Here is a couple recommendations:
- https://github.com/Extrawurst/gitui - a very handy and straightforward GUI client
- https://github.com/chriswalz/bit - essentially cli, but with auto-complete and hints
- https://github.com/jonas/tig - ncurses-based text-mode interface for git
- some of our developers had success the built-in clients of VS Code and JetBrains IDEs
Practice
You can practice general git work using the following website:
https://learngitbranching.js.org/
Especially these sections are important:
- Main: Introduction Sequence
- Main: Ramping Up
- Main: Moving Work Around
- Remote: Push & Pull -- Git Remotes!: Sections on
fetchandrebase
Braiins git conventions
We are very particular about how we organize work in Git, you need to pay attention to the following:
- commit formatting
- commit locality
- branch naming
- merging
Start by reading the following document:
http://pool.pages.ii.zone/main/braiins_standard/tools/git.html
TIP: Remember that the CI rejects commits that are created with other emails than your work one, make sure to use
git configto set it correctly before committing anything
Commit message lengths, prefixes and suffixes
A commit should have at least one prefix. The first prefix should specify which project
(or part of the Braiins codebase) a commit is related to or its topic, while the optional second can be used for:
a. Further defining scope within a project (for example: first prefix -> cargo workspace, second prefix -> particular crate)
b. In the context of bosminer, for specifying which machines are affected by the commit
Here is a couple example prefixes:
bosminer:stratum-proxy:bosminer+:docker-spider:stopwatch:bosminer+: x17:
Some Braiins crates end with the suffix -plus. As you can see in the examples above, in commit messages,
we substitute these with the + sign. This also helps save characters
Prefixes should be separated by a colon and a space from each other and the title of the commit, and the title of the commit should start with a capital letter:
# Incorrect
bosminer+:x17: Fix a bug or implement something
bosminer+: x17: fix a bug or implement something
bosminer+: x17:Fix a bug or implement something
# Correct
bosminer+: x17: Fix a bug or implement something
If a commit has to be related to two different things (such as two Antminer model lines), separate them with a comma without a space:
bosminer+: x17,x19: Fix a bug or implement something
Length limitations apply:
- The entire first line of the commit (ie. prefixes & title) has to be at most
72characters long - Break other lines at about
80characters for readability
Remember that the commit title length limitation also includes the Redmine ticket number suffix
Commit message content
In Braiins, for clarity and consistency, we write commits in the imperative mood. What this means in less linguistic terms is that you write that a commit does something instead of you did something in a commit. If you struggle formulating your changes in imperative, try answering the question:
"What will this commit do, if I apply its changes?"
your answer will be something like:
"It will make <project/topic/crate> check dependencies in the generic run() impl on Executor"
Then you take the project/topic part, making it your prefix, and take the rest after it with first capital letters, forming your commit message (optionally sticking ticket number at the end):
crate: Check dependencies in the generic run() impl on Executor #6667
This is 69 characters, so we fit in the length limit.
Per the example on Pool pages, the body (if required) of the commit message should be also in imperative, split into bullet points:
topic: Imperative subject description #1234,#5678
- #4321,#8765
- refactor this method because ....
- add new implementation of XYZ to support new protocol
Commit locality
All commits, unless they are code-moves and or you have a very good reason for breaking this rule, should at least compile, and hopefully also work
Moves/rename commits are a special class of commits: if you are moving a large chunks of code and at the same time you are changing the code, you should SPLIT IT into two commits: one doing the code move and one doing the actual change. The reason for this is simple:
-
the code move + code change in single commit is impossible to review, because (at least in gitlab) you don't see what was moved and what was changed
-
the code is not rebasable - if you have conflict you have to first undo your changes, then resolve the conflict, then apply you changes back
Furthermore, a commit should have a reasonable scope, there is a hard requirement and a soft requirement:
The hard requirement is that a commit can only modify proprietary code, or open-source code, but not both at once.
For projects, which have a variant with the -plus suffix, the non-plus version is generally considered open-source
and the plus version is proprietary. Projects placed under the open/ folder in the root of the repository is all
considered open-source. A commit modifying both proprietary and open-source code would prevent extracting just the
open-source git history into its own separate repository.
The soft requirement is that a commit should have one clear goal, if your commit changes too many projects at once, or does too many things at once, the reviewer of your merge request may request you to split it into logical components
It also probably a bad idea to change code and database migrations in one commit in case either would need to be reverted.
Branch naming
Branch naming is documented in the pages link as such:
xxx/change_description
Where:
xxx: is your GitLab handle (e.g. pmo)
change_description: should help to recognize the work being done on that branch easily. It can include the name of the app or library being worked on.
Don't use any letters with diacritics or special characters other than /-_
If you have a branch with multiple segments, such as bos/frontend/translate-to-finnish, make sure that
there doesn't exist a branch whose name would end with one of the non-terminating segments, such as bos/frontend.
This will cause trouble in git and Gitlab.
Some prefixes have special meaning and are parsed by the CI/CD, affecting which pipelines and jobs are run. If you are unsure, ask your team-leader, DevOps, or other colleagues.
Merging
In Braiins, we build a linear history by using git-rebase. The rebase command is a useful multi-tool for editing Git history, and you should become very familiar with it.
Namely, make sure you know and can use the following:
- Rebase in place of merging (Link 2) - Merge commits are considered invalid, and they make viewing history in tools like tig or gitk confusing
- Rebase for editing history (interactive rebase)
essentially the only tool you need for editing history, remember that you can use it to do the following:
- edit or redo commits
- change commit message
- reorder commits
- drop commits completely
- squash several commits into one
- Editing a distant commit with
--fixupand then auto-squashing - Pivoting the branch your dev branch originates from with
--onto
TIP: Use
git rebase --ontoif the branch your branch originates from had its history altered. In Braiins, it is highly unlikely this will happen withmaster, but you may be developing on top of a branch of a colleague(s) which is a WIP
TIP 2: Also look into cherry-picking
TIP 3: When resolving conflicts during a rebase, keep in mind that the terms
ours/usandtheirs/themused by Git are the opposite of what you might expect.If I am on branch
bos/lho/fix-somethingand I do agit rebase master, thenours/usismasterandtheirs/themisbos/lho/fix-something.This is because
usis always the base branch, and when rebasing, its state used as a starting point upon which changes from the rebased branch are applied. To make the situation confusing, some GUI clients swap the terms around when displaying conflicts to the user, so if you use one, verify which is which.