Recently I was on boarding a new FPGA developer and one of the tools we use often came up. It was not one of the FPGA implementation tools or a simulation tool but a version control tool which is often used across the software and embedded world Git.
What is Git?Git is a version control system, this means it helps manage and track versions of our source files and projects. Git enables multiple people to be working on the same project concurrently, working collaboratively how it does this we will examine later.
One of the key things about git is that it is distributed, that is each developer has the local copy of the full development history on their machine.
Like Linux Git was created by Linus Tovald's and the story of its creation is very interesting, it is to long to go into in this project but you can read about it here
Git is very powerful and sometimes developers get a little scared of some of the more advanced features but through this project we are going to see Git is nothing to be scared of.
Before we go to much into this project though I want to explain a few of the key concepts for git.
Key ConceptsRepositories - This is the foundation of the git system a git repository (repo) contains all a projects files with their development history. We can store repos either locally or in the cloud, it is normal for the local repo to be where we work day to day and the cloud to be the repo we push, merge, clone and branch from.
Clone - Cloning a repository is what we do when we want to start contributing to a project. Cloning a project downloads all of the data within the git repo. There is another method sometimes used when we want to start collaborating called the fork which is explained below.
Commits - Commits allow us to keep track of the changes made to the project. As we write our code Git will keep track of the changes and show which files have been changed. When we are ready to take a snapshot of these changes we can perform a commit which will commit them to the local repo. One of the crucial things to do when we make a commit is to create a detailed message saying what is in the snapshot and why. As every commit is a snapshot we need to be able to uniquely identify it, we do this using the Git commit hash which is created for commit and is based off the content of the commit, commit message and several other factors which mean each commit is as unique as a fingerprint.
Tags - Tags are used to reference specific point in a repos history. This could be release points e.g. v1.0 or modifications to support newer development tools. e.g. moving from Vivado 2023.1 to 2023.2
Push - While a commit stores the snapshot to our local repository a push sends the changes in our local repo back to the remote repository. This is one of the key elements of collaboration in Git as it allows changes made by one developer to be accessed by others.
Branch - Another strength of Git is it enables non-linear development, that is we are able create a snapshot of the repo at a given time and start developing it in a different direction to the main development. This could be to add in new features or look at experimental "what if" scenarios. This allows the main development path to not be impacted by these developments. In many organisations Adiuvo included each developer normally has their own branch which is then merged into the main.
Merge - Once we have complete the development of the new feature or fixed the bug in our branch, we need to merge it back with the main development branch. Merging combines the two repos, it is during this operation that we might see some issues if there are conflicts. In which case we will need to resolve these.
Pull Request - A pull request is created to propose pulling the changes from one branch to another. It is normal at this point to have a review of the code being pulled to ensure its quality and that is addresses the requirement, demonstrates the fix etc before the pull occurs.
Fork - Forking a repo takes a copy of the repo, enabling changes to be made without impacting the original development as it is completely separate. Forks are often created to take projects off in new directions e.g. new hardware platform based on the previous. They are also used for people to contribute to opensource projects. People will often fork the project create new features and then make a pull request to merge the new capabilities back into the original repo.
Check Out - Checkout enables us to checkout either another branch, or previous commit. We can also use the checkout to discard changes and restore to a previous version. We can also use the checkout to create a new branch, if one does not already exist.
Detached Head - Using Git we are able to check out any previously commit such that we can roll back if required. This can be useful to determine if something recently introduced has caused an issue / bug or if we need to be able to recreate a specific build configuration for example. When our repo is no longer pointing to the tip of the branch we are working on, we call this a detached head situation.
Work Flow - While there are many different methods of working with Git the most common approach is
- Clone the remote repository to create a local copy
- Create a new branch to develop within
- Make the changes and verify them in the branch
- Push the changes to the remote repository branch
- Create a pull request
- Merge the pull request
Now we understand a little about the Git and its key concepts lets get started looking at how we can work with Git using some FPGA source code.
While there are many different front end GUIs and different hosts of remote repositories for this project we are going to use the git cli and github. Once we understand the fundamentals we can then start working with other interfaces and tools for example.
Installing GitTo get started with this project we need to install he git cli. this can be downloaded from here.
Once installed if we open a Git bash and run the command
git --version
We should see the latest version for your development environment.
The next thing we need to do is create a github account - if you have an existing account you can skip this step.
Enter your email, password and account name
Complete the sign up process and view your profile and you will see something similar to below.
With the Git tool installed and a remote repository it is time to start creating and working with our source in Git. I am going to use the HDL and Python source from the previous project which looked at cocotb.
Take the files attached to this project and create a new directory in your file system and copy / save the files into it. I called mine Hackster_Git
Inside should be the files
The next thing we need to do is initialise a new Git repo, add the files and perform the initial commit to the local repo.
To initialise the repo we can use the command
git init
This will report a empty repo is initialised as we have not yet added any files.
We can add the existing files in the directory to the Git repo using the command
git add .
This will add in the files in the directory.
The final thing we need to do is perform the initial commit with the command
git commit -m "initial commit"
The -m enables us to store the message.
We are now in the position we have created our initial local repository, added the files and performed the initial commit.
What we need to do now is push this to the remote client.
In our remote repository, in this case GitHub we need to create a new repository - do not add a readme, license or git ignore file as this will cause conflicts which need resolving.
Copy the URL of the created repository which for me is https://github.com/adiuvotraining/training_git
In GitHub this will look similar to below
We now need to line the local and remote repositories
git remote add origin https://github.com/adiuvotraining/training_git.git
With the repo linked the next stage is to push the local repo to the origin, as we are doing this for the first time we will see requests for authentication and authorization to access the remote repository.
git push -u origin master
Authorise the credential manager
Once the authorisation is completed you will see the repo has been pushed.
Refreshing the remote repo will show now the files
We are going to say this is the initial baseline and is version 1 of the development to do so I need to add a tag to this to create a simple reference point.
git tag v1.0
git push origin --tags
With version 1 tagged we are now we are able to get started developing.
Creating a BranchFor this project lets assume we need to make some changes to the UART baud rate. As such we need to make changes to the top level, and debug_top python file.
Before we can do this however, we need to create a branch for development to create the branch we can use the command.
BUT before we create the brnahc we need to make sure we are branching from the right starting point. As such we need to ensure we have pulled the master / main repo first.
git pull
git checkout -b uart_update
You will see the new branch being reflected on the git client
We are now able to make changes to the files. The changes are pretty simple all we need to do is change line 67 of top.vhd
Along with lines 25 and 26 in the debug_top.py
With the changes made we can run the simulation and verify they are correct, but for the purpose of this project we will assume they are OK.
The next stage is to commit the files we have changes, we can see the status of changed files
git status
To add the files we can add by individual file name or use the. command
git add .
Running a git status will show the modified files have been added
We can then commit the changes to the branch
git commit -m "UART Baud Rate Lowered"
What has changed will be reflected in the message
We can now push this to our branch - as this is the first time we are pushing the branch we need to set the upstream branch. After this has been done for the first time we an just use the command git push.
git push -u origin uart_update
We should now be able to see the uart update branch on the remote branch.
Under each branch you should be able to see the differences between the original uart baud rate and the updated baud rate.
The next task is to merge the changes to the main development branch. We cna do this creating a pull request.
Pull RequestTo perform a pull request we need to use the remote repository
In GitHub select the pull requests tab - you will see it automatically has detected the changes we just pushed to the uart_update branch.
However we are going to use the long method so we understand the steps. Click on the New Pull Request Option.
Select the UART Update for the comparison
This will compare the selected branch against the main repo, we should be able to see the changes we have made to the files.
Click Create Pull Request
Enter the reason for the pull request, in this example I have explained it is because of limitations on hardware etc.
Create the pull request, and on the next screen merge the pull request
Confirm the merge
This will complete the merge requst
We can now see the changes in the main branch contains the merged changes
Of course we also have the tag for V1.0 which we can select which previous version if we want
We can easily view this in GitHub by selecting the verison 1.0 tag
If we need to check out a previous version or commit we are able to check it out using the command
git checkout v1.0
This checks out the version 1 and puts us in a detached head state. DO NOT do any development on this checkout.
Hopefully now we understand a little more about Git and are a little more comfortable working with it!
Comments