-
Notifications
You must be signed in to change notification settings - Fork 0
Git
You are reading this guide on the wikipage for the DNT-Beginner2Expert repository on github. What does git do exactly? Git is a version control system, which means it keeps track of your files and any changes you make to them. We use git to keep track of changes in our code and you will need to use git when moving on to the next step in this guide. In this section we will give a short overview of how git works and how you can use git, for more information you can always visit the docs. Before we continue, you need to set up git.
If you are reading this, that means you already have a git account with access to this repository. The next step is to install the git package on your system, you can do this by running the following command.
sudo apt install git
After you have installed the git package, we need to set up the SSH keys, that are used to verify that you have permission to access this repository. This access is restricted to make sure not just anyone can access/change the code in the repository.
To verify that it is actually you accessing a repository git uses SSH keys based on the public-key cryptography. The SSH key system consists of two keys, a public and a private key pair. You can generate such a keypair by using the following command.
ssh-keygen -t ed25519 -C "<your-email-address>"
You will be asked a series of questions and you simply choose the default options by pressing enter. This will generate two keys in the directory ~/.ssh. The first key will be named id_ed25519 and is called the private key, never share this key! The second key will be named id_ed25519.pub, this is the public key, it is safe to share this key. The fundamental idea in public-key cryptography is that you can prove that it is you that created the public key. This way, if git knows that a certain public key is linked to a certain account with access to a repository, if you can prove that you are the one that created the public key, git knows that you are thus allowed access to that repository.
Public-key cryptography is based on one-way functions, functions for which it is easy to compute the output given a certain input, but for which it is infeasible to compute the input to that function given it's output within a certain time. These functions are used in a certain way that they guarantee that a ciphertext that is encrypted using the public key can only be decrypted (in a feasible amount of time) by someone that has access to the corresponding private key. Git can use this system by encrypting a certain text using the public key corresponding to a certain account. If you are able to decrypt that ciphertext and return the original text, you have proved that you are the owner of that account (or at least associated with it in some way). If you want to learn more about how this is done, take a look at the Diffie-Hellman key exhange.
To complete this protocol, git thus needs to know the public key, you can provide git with the public key by adding it to your account under your account settings in the "SSH and GPG keys" tab. There you can simply click on "New SSH key" and paste the contents of you public key. Lastly you need to add your private key to your ssh-agent, which will complete the protocol from your side using the private key. You can first make sure the ssh-agent is running with the following command.
eval "$(ssh-agent -s)"
Next you can add the key to you ssh-agent with the following command
ssh-add -K <path-to-your-private-key>
You should now be able to use git to access this repository or make new repositories yourself.
In this section, try following along and see for yourself what these different commands do.
You can initialize a new git repository by locally making a new directory and the running the command git init. This will create a .git directory in your repository, which git uses to store information about your repository. If you want to see this directory you need to use the -a flag with the command ls, which indicates you want to see all files, including hidden files (files/directories that have a name starting with a).
Now execute the
git status
command. You will see several things, the first is 'on branch master'. So what does this mean? When working on the same project with multiple people, it is useful to have different branches. Often a project has a main or master branch which contains the current state of the project. When a new feature is to be added to that project a new branch can be made, branching away from master. All changes made will now be stored in that branch. A new branch can be made with the command
git checkout -b <branch-name>
To list all local branches you can use the command git branch (what the difference is between local branches and remote branches will be clarified in the next section). To switch branches you can use the command git checkout <branch-name>. Later once the feature has been completed and it is verified everything works as it should, the branch can be merged back into the master/main branch, adding all the new changes. By using branches, different people can working on different features at the same time without having conflicts while working in the same files.
After git status command in the previous step, the next thing you saw was "No commits yet". This
Git stores changes both locally (on your computer) and remotely (on a remote server). Lets make a change in your local repository by adding a new file. Storing this change remotely is a process with several steps. First run git status. Now you will see the file you added under untracked changes.
Now you can to execute the command git add <filename>, or instead of adding only specific files you can also use the following command in the root of your project to add all changes.
git add .
This command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. However, git add doesn't really affect the repository in any significant way—changes are not actually recorded (stored) until you run git commit. If you now run git status, you will see your file under "changes to be committed".
So what is a commit? You want to store the progress you make when implementing a feature in small steps. This way if you make mistakes, you can revert those mistakes and go back to a certain state in the project instead of having to revert all changes. Commits are small intermediate steps, make a habit of committing code often, because when you make a commit this will actually store the changes you made in the staging area locally. After running the git add command, run the
git commit -m "<message-describing-your-change>"
to store the change on your branch locally.
Changes thus far are only stored locally. The command git push pushes the local changes to the remote server, but when setting up a new repository Git first needs to know to what remote repository you want to push your changes and what remote branch you want to push your local branch's changes to. You can create a new remote repository to push your changes to on the github homepage. You can give the repository a name and some additional settings that will be clarified for later. For now simply choose the default settings and click on "create repository". You can now add the remote repository with the command
git remote add origin <url>
, you can copy the URL from the homepage of that repository. This remote will now have the name "origin". If you try the command git push now, you will get the message: "The current branch has no upstream branch.". This is git telling you it does not know to what remote branch you want to push your changes. The full format of the git push command is git push <remote-name> <local-branch-name>:<remote-branch-name>. This will automatically make the new remote branch if it does not exist. If you leave away these specifications git will assume that the local branch name is equal to the remote branch name. However, you might not want to specify which branch you push to on every command, assuming your local branch always pushes to the remote branch with the same name, which is usually the case. Therefore you can set an "upstream branch" to which your branch pushes on default, this can be done using the command git push --set-upstream <remote-name> <local-branch-name>:<remote-branch-name> or git push -u <remote-name> <local-branch-name>:<remote-branch-name>. Since the remote branch name is usually the same as the local branch name you can also use the command
git push -u <remote-name> <local-branch-name>
, where git will assume the remote branch you are pushing to has the same name as your local branch. After specifying an upstream branch, you can simply run the command
git push
and git will push the changes from that branch to the remote upstream branch.
At a certain point you will want to merge your branches, probably you will want to merge a branch into the main/master branch. This is something that can be done by creating a pull request. You can make a new pull request by navigating to the "Pull requests" tab in the menu of your repository. Here you can press "New pull request". In the menu that follows you can select which branch you want to merge into which other branch. Next you can press create pull request and add a comment to describe the changes you have made. Here you can also assign other members as reviewers. This is necessary, because usually a repository is set up in such a way that no changes are allowed to be made to the master/main branch unless enough people have reviewed that code (checked if it works properly). We do this to make sure that no bugs are ever pushed to the master/main branch. Finally after assigning another member to do a review (they will get a mail notifying them), you can press "Create pull request". After receiving two approvals it will become possible to finally merge your changes to the master/main branch.
A merge conflict happens when you are merging into a branch and git does not automatically know what changes to use, the changes in the branch you are merging to, or the changes you made in your branch.
I will now shortly describe a situation in which this scenario can occur. Lets say Alice and Bob both branch away from the master/main, and edit the same file. Alice merges her branch into master/main first and has no problems, her changes to that file are merged into the master/main branch. Next, Bob tries to merge his branch with master/main, but a problem occurs. Git does not know whether it should take the changes Alice made to the file or the changes Bob made to the file in the resulting merge of these two branches. This is called a merge conflict. To solve the merge conflict it is necessary for either Bob are Alice to tell git what changes are to be approved.
Ideally you do not want to create a merge conflict on the master/main branch. So before creating a new pull request it is best practice to first pull the master/main branch on your own branch, this can be done by first making sure you are on your own branch (you can always check this using git status) and then using the command
git fetch <remote-name>
, this will update all local branches to their counterparts on the remote server. Now you can use the command
git merge <master/main>
to merge any changes made on the master branch in your branch. Now you can fix the merge conflict there, and then create a pull request for the final merged branch, preventing merge requests on the master/main branch. Instead of running git fetch and git merge you can also run the following command.
git pull <remote-name> <master/main>
Under the hood it will do something very similar to writing out the two commands explicitly.
Note: in this example the remote-name is "origin".
So how do you fix the merge conflict after running the command git pull <remote-name> <master/main>? You will see a message indicating in which files the merge conflict has occurred. In the files where the merge conflict has occurred you will see something like this on every location there is a merge conflict:
<<<<<<< HEAD
Your changes
=======
Changes from master
>>>>>>> SHA-1 of the last commit on the branch you are merging into your branch (in this case master)
You have to go through all these conflicts and edit them such that all that is left at the location of the merge conflict are the changes you want to have in the final merged result. IDEs can make this process easier. After solving the merge conflict, add, commit and push the final changes to your branch and create the pull request. After editing solving a single conflict your code will thus usually be either:
Your changes
or
Changes from master
Lets try creating a merge request in a new repository so we can try all of this out, feel free to use the git status command anywhere during this process. First make a new directory in which we are going to work and change our working directory to that directory.
mkdir ~/Documents/testing_repo &&
cd ~/Documents/testing_repo
Now add a simple textfile to that directory named test.txt with the content "This is a test.".
echo "This is a test." > test.txt
Initialize a local git repository.
git init
Add and commit the changes.
git add test.txt &&
git commit -m "Initial commit"
Next go to github, log in and make a new private repository, you can choose your own name. A description is unnecessary and you can leave all other default options as they are. Click on Create repository. Now we have made a remote repository. Lets push the content of our local repository to this remote repository. The instructions for doing this are shown in the browser on github after you created the new repository. It should look somewhat like this:
git remote add origin git@github.com:<username>/<repo-name>.git
git branch -M main
git push -u origin main
Execute these commands and take a look at the repository in the browser after reloading the page. You should see that you are on the main branch and that the repository contains one text file. You can click on it to see the contents we wrote to that file.
Now lets pretend we are a team member Bob working on this documents. Make a new branch (with the name bob), change the contents of the first line to "my name is bob" and then add and commit the file.
git checkout -b bob &&
echo "My name is Bob." > test.txt &&
git add test.txt &&
git commit -m "Changed the contents of test.txt"
He now pushes his changes to the remote.
git push -u origin bob
Lets say that Alice is working on the same document. Lets change the directory back to main (this is where Alice would start when working).
git checkout main
Then she executes roughly the same commands, but writes a different content to the same line in the same file.
git checkout -b alice &&
echo "My name is Alice." > test.txt &&
git add test.txt &&
git commit -m "Changed the contents of test.txt"
She now pushes her changes to the remote.
git push -u origin alice
After doing this take a look at the repository in the browser. You should now be able to change the branch your looking at from main to alice or bob. On this branch you should now see that the corresponding contents has been written to the test.txt file on that branch.
Go to alices branch. Alice want to merge the changes she made to the main branch. She can do that by pressing "pull requests" in the repository menu in the browser. There you should see a message like "alice had recent pushes x minutes ago". Press "Compare & pull request". Write a description and press "Create pull request". Git will now check if it is able to merge the changes into master. Usually first you must first get two reviews but for now, lets pretend we got those review and press "Merge pull request" and "confirm merge". You can now press on the "code" in the menu on the top to go back to viewing the content of the repository. You should be on the branch main and if you click on test.txt you should see the contents that Alice merged into the main branch.
After Alice was doing her work, Bob also wants to merge his changes into main. He wants to fix any potential merge conflicts on his own branch, so he first merges the main branch into his own branch. We do this by first pretending we are Bob by checking out to his branch.
git checkout bob
Then we merge the main branch into ours.
git pull origin main
If this is the first time you execute this command, you will probably get a message now asking you what strategy you want to use for solving conflicts when executing this command. If this happens, choose the top strategy and execute the command git config pull.rebase false and execute the command git pull origin main again.
You should now see a message warning you that a merge conflict has occurred in test.txt.
To solve the merge conflict, edit text.txt using an editor of your choice just as we have described above, by choosing the content that you want it to make into the main branch.
Now add, commit and push the changes.
git add test.txt &&
git commit -m "solved merge conflict" &&
git push
Now create and accept a pull request just as we did with Alice. You have now successfully solved a merge conflict as Bob!
Tip: Always update your own branch with a command like git pull origin main before making a pull request!
Sometimes you will be assigned to do a review, this means you will go through the pull request and check it. If you find mistakes you can leave behind comments, only after the code is perfect, you will approve the pull request. You can take part as a reviewer by navigating to the tab "Pull requests" in the navigation bar of that repository. There you can press "Review changes" and complete a code review. After completing the review, you can either just "comment", "approve" or "request changes". If you are the last reviewer you will get the option to "Merge pull request". At this point there will have been other reviewers that have approved the code. If it looks good to you, go ahead and merge the branch into master/main.
If you want to start working on an existing project, simply clone the project with the command git clone <url>. This initializes the project, you can now simply work on it as you would after initializing the repository yourself. Clone this project into a new directory by copying its ssh URL from menu that appears when you press on the green "code" button on the repository homepage and executing the command git clone <url>.
git clone git@github.com:IntelligentRoboticsLab/DNT-Beginner2Expert.git
After executing this command, you should now have a new directory in your current directory, called DNT-Beginner2Expert. If you move into that directory and execute the ls -a command, you should see all the files and directories that are present in this repository.
When you open a new repository on github you get the option to add a README.md file and a .gitignore file. Besides in the code itself, documentation is often written in README.md. A README.md in the root of a project usually provides information about how to use the project install it as a package, etc. A .gitignore file is a file in which you can specify which files or directories you do not want tracked by git (this can be useful for credentials, log files, build files, etc, that should not be on git). For more information on the syntax take a look here.
TODO
These are some other git commands you might want to take a look at:
- git diff - useful for solving merge conflicts
- git log - show commit log
- git rm - remove files from git
- Home
- Organisation
- Working with Ubuntu
- Git
- Python
- Rust
-
Robot usage
- Setting up DNT
- pyNaoqi Setup Guide
- Connecting and compiling
- Teamcopy
-
Playing a match
- Setup
- Rules
- Working day
- Gamecontroller
-
Framework
- Overview
- Taal
-
Framework Applied
- Dummy
- Assignment