This guide was written to lay out the basics of Git collaboration for some software projects to be done collaboratively in the CIRS lab.
To refresh your knowledge on Git usage and functionality, you can review this guide.
This guide takes inspiration from A successful Git branching model
along with Conventional Commits and is also loosely based on the Git flow that
we used while I was at Levita Magnetics. The one important distinction, is the use of git rebase before merging a feature
branch into a release or working branch, to avoid polluting the history of the repo. This is entirely a preference of the author and
open to debate.
Maintaining a standard Git flow allows for better control of the current software version, tracking development progress, seeing what
each team member is working on, keeping code more organized, and improving control over integration and validation of deliverables.
It also allows for other tools to be run with better results, such as git bisect or different kinds of automated tests.
The workflow should mainly depend on two branches:
origin/main: this is where the code that runs during field tests and live trials. This version must be ready to be
deployed, used and (within reason) bug-free, with all its dependencies updated and well documented.origin/develop: this is the development branch, on which functionality is added and the software progresses. This
branch should be used for dry or indoor water tests, and if ready to use, should be merged to main through a release branch.
Creating a branch off another one is very simple, to create a new branch and move to it, run git checkout -b <branch_name>.
Once you've done some changes, use git add <file1> <file2> to stage your changes. If you want to add all
changes and untracked files under the current directory, you can use git add . or if you want the same but for the whole
repo, (carefully) use git add *.
Then to commit those changes to the HEAD of your local branch, run git commit -m 'commit message'. To update the remote
repository, run git push.
Once you are ready to open a pull request you should squash all
your commits and then rebase your branch, to do this, first make sure your branch and any others are up to date by running
git fetch --all --prune, then to squash your changes w.r.t. merge-base, run git rebase -i $(get merge-base HEAD @{u}),
where @{u} should automatically pick up the name of the upstream branch. Then, in the text editor, for all commits except
the first, replace pick with squash or s, after that is done, you can also amend the commit message
with reword or remove a commit with drop. In this step, you might be prompted to solve any conflicts there might be.
After you are done, save, exit and then run git push --force. It is very important that the --force flag is
given any time you push after a rebase, as we are rewriting the branch history and we want to update it in the remote too. You can now
go to your branch in GitLab or GitHub and create the pull request.
While the PR is opened, any changes to the branch by yourself or other developers, should be done using git merge, to
preserve the history.
Once the pull request is approved, if there were any intermediate commits, squash and rebase again, and then merge them into the
upstream with git merge <upstream>.
There are three types of branches apart from develop and main:
These branches are used to develop repository features. Once this feature has been completed, the branch must be merged to
origin/develop through a Pull Request.
They are branches from develop or another feature branch. They must merge to develop.
They don't have a particular naming convention*, but names should be descriptive and use only hyphens, for example,
holonomic-control, motor-driver or pathplanning-task.
Once the last push to origin has been made, a PR must be opened from BitBucket/GitHub and at least 2 people must be
marked as reviewers, who are not the author of the branch. The PR should not be merged unless there's approval from at least one
reviewer. Once the PR is accepted and merged, the associated branch should be deleted from origin.
* If the repo were to be connected to an issue tracking system, it could require you to write the number of the ticket as part of the branch name so that it can track the branch.
These branches are used to test and validate all features created up to that moment, in preparation for a field test.
They are branches from develop, and they must merge to origin/develop AND to
origin/main. Release branches can include new changes to the code, so it is important that they are then
merged back to develop, as to not mess up the commit history.
The naming convention is release-*. These branches could also be tagged so that a version pipeline can run over them.
These branches are used in case a critical failure is detected during a field test.
They are branches from main. They must merge to origin/develop AND to
origin/main.
The naming convention is hotfix-*.
The naming convention for commits should be as follows:
feat(*):*: indicates that progress has been made on a particular feature, for example, feat(task0 launch)
could indicate that the Task 0 launch has been completed. This is the commit style that should be used most frequently in feature branches.fix(*):*: indicates that a bug has been fixed, for example, fix(vel 100 in reverse) could indicate that
a bug where the throttle input always went at 100% power in reverse has been fixed.release(*):*: indicates that a new release has been completed.BREAKING:*: used when changes are added that could break functionality in other repositories, for
example, if the format of a message that must be sent by nodeA but received by nodeB under another repo
is modified, or a major dependency has been bumped to a newer version.These commits should be completed as follows:
<type>[scope]: <description>
[optional body]
Where type is one of the previously mentioned types. scope broadly indicates what has been changed,
description can provide more details about what has been changed and body any other relevant information
worth mentioning.
Here are some common commands you might want to use:
git switch <branchname> - move between branches.
git status - see files which are not staged or committed, or what their status is.
git diff | <branchname> - by itself, shows the diff of files changed but uncommitted or unstaged. If a branchname
is given, it will show the difference w.r.t. its local tip.
git commit -amend - edit the commit message.
git reset --hard HEAD~1 - undo the last commit or merge and discard changes.
git reset --soft HEAD~1 - undo the last commit or merge and keep changes.
git reset --hard HEAD, then git pull - remove all uncommitted changes and then pull remote tip into the
current branch.
git log - lists the commit history.
git fetch -p - sync the branch list.
If you want your bash shell to show the current branch you're at, add the following in your .bashrc file:
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\[\033[01;32m\]\u@\h \[\033[34m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ "