Collaborating with Git

GitHub

In order for multiple people to work together on programming our robot, we place all of our code on a website called GitHub. If you don’t yet have a GitHub account, you should create one now.

In GitHub, you create a “repository” for each of your coding projects. For example, our team has a repository for each year of robot code. Once code is placed in a repository, anyone given access to the code can download it and suggest changes.

Creating a Repository

To create your own code repository (after logging in to GitHub), click the “+” button on the top right of the site. Then, press “New repository.”

Next, choose a name for your repository and give it a description, if you’d like. Then decide if the repository should be public or private.

  • Public repositories are visible to anyone on the internet, but, by default, only you can modify the code.
  • Private repositories are only accessible to you.
  • For both public and private repositories, you can choose to give specific GitHub users editing/viewing permissions at any time.

Do not place a check by the “Initialize this repository with a README” option, and ensure that “Add .gitignore” and “Add a license” are set to “None” for now. I’ll get back to what these do later on.

Next, I’ll show you how to add code to this repository.

Git Configuration

To interact with a GitHub repository, we use Git, which is a set of many commands for making it easier to manage our code. Start off by going to the Git download page and choosing your computer’s operating system. This will automatically download the Git installer. Double click on the installer once it’s downloaded and follow the default installation settings. Be sure that the “Git Bash Here” option is checked when you get to this screen:

Once installation is finished, create a folder for a new C++ program, open the folder in Visual Studio Code, and write a simple program like this:

#include <iostream>

int main() {
    std::cout << "Test\n";
    return 0;
}

If you forget how to set up a Visual Studio Code project, you may want to refer back to C++ Development Configuration (Windows).

Creating a Local Repository

Next, let’s use Git to create a local repository for your code (local means that the repository’s just on your computer, not on GitHub). There are two things we should do first, though. These are optional, but highly recommended.

Creating a README File

It’s usually recommended to give your repositories a “README” file that contains a description of your code. These files are often made using the “markdown” file format (ending in “.md”), which allows you to format the file with headings, links, images, and more.

To create the README file, right click inside of your project’s folder, and navigate to New > Text Document.

Then, rename the text file to “README.md”. To open the file, right click it and navigate to Open with > Notepad. If the Notepad app doesn’t show up as an option for you, you may need to click “Choose another app” and select it from there.

Into my README file I’m writing:

# Test
Testing out Git and GitHub.

In markdown, the “#” character indicates that its line of text is a heading. For a complete list of markdown syntax, reference the cheat sheet here.

Creating a .gitignore File

Sometimes you’ll only want some files in a folder to be placed in a code repository because certain files can clutter up a repository without providing any benefit. For example, the only important component of the little project we created is the main C++ file. The “main.exe” file and all of the configuration code in the “.vscode” don’t really need to be added to the repository since they aren’t crucial to the structure of the project.

To prevent them from being added to the repository (when we create it next), create a new file (like we did before) and name it just “.gitignore”. Then, open it with Notepad.

Any files or folders to ignore go in their own line in the .gitignore file. For example, to ignore main.exe and the .vscode folder, type this:

main.exe
.vscode
Initializing a Repository

Now we can create our local code repository. To do this, right click inside your project folder and press “Git Bash Here.”

Then type “git init” and press enter. Now the repository is created!

Adding Files to a Local Repository

While the repository is created, no files have been added to it yet. To add all files from the current directory (except those in the .gitignore file), type “git add -A” and press enter. Then, this needs to be “finalized” by commiting the files to the repository. To do this, enter the command “git commit -m [message]“, where “[message]” is a description of the files added or modified.

At this point, you may run into an error where Git does not recognize you or your computer.

If you get an error similar to the one above while attempting to commit your code, just follow the commands they give you (“git config --global user.email [your email]” and “git config --global user.name [your name]“) and then try to commit again. Be sure to put the email address that is linked to your GitHub account.

Tip: Usually the message for commiting files is written in the “imperative” mood (like a command). For example, my message in this case may be “Add all files”, not “Added all files”. I know, it’s a bit awkward; that’s just how it’s done.

Adding a Remote Repository

Now we need to send our local repository to the “remote” repository at GitHub (the one we created at the beginning). Once our code is there, anyone else with proper permissions can see the code and suggest changes to it. This is how we can have all programmers in the robotics team work together on the same code project.

The first step to this is adding the remote repository with Git. To do this, use the command “git remote add origin [URL]“, where “[URL]” is the URL of the remote repository. To get this, just use the link provided on your repository page:

For me, I typed “git remote add origin https://github.com/MatthewATaylor/Test.git” to add the remote repository.

Note: To paste text into the Git command line, use Shift + Insert (not Ctrl + V).

Pushing to a Remote Repository

Last, we send our code to the remote repository by “pushing” it. The first time you push to a remote repository, use the command “git push -u origin [branch]“, where “[branch]” represents the branch to push. I’ll discuss Git branches later, but just know for now that the current branch is listed in a blue font and inside parentheses:

Right now, your current branch should be “master” like mine, so the command to type would be “git push -u origin master“.

When performing this command for the first time, you may be required to sign in to your GitHub account. Then, after running the command, you should be able to see your code in your remote repository, not including the files from the .gitignore:

You can also see that the README file is displayed at the bottom of the file list. For me, the word “Test” is turned into a heading because I used the “#” before it.

Modifying Files

Now let’s modify the test program to this:

#include <iostream>

int main() {
    std::cout << "Another test\n";
    return 0;
}

(Be sure to save your file after making modifications)

To add the modification to my local repository, we first use “git add -A” and then we can commit the change with “git commit -m "Change printed text"“.

Our remote repository has already been added, so we don’t need to worry about that. All we need to do now is push the code. Since we’ve already pushed the code once with “git push -u origin master” (which pushes the code and configures the master branch in the remote repository), we just need to use “git push“. Now the code in your remote repository should contain the new change we made.

Branches

If you ever need to make a large change to a program that may cause it not to work properly, then “branches” can help out.

Branches allow us to create a copy of all the code in a repository. In the case of making a large change to a program, the change can take place in a branch of the main project. This way, a major mistake doesn’t break the only copy of your code.

Of course, undoing and redoing changes is always a possibility when not using branches, but that can be a bit harder to work with. Let’s take a look at how branches can be used instead:

Creating a Branch

To create a branch, type “git branch [branch name]” where “[branch name]” is the name of the branch you want to create. I’ll create a branch called “dev” (short for “development”) using the command “git branch dev“.

After doing this, you can see that the branch was successfully created by using the “git branch -a” command, which lists all of the branches in your repository. Here’s what I see after using that command:

The asterisk and green font indicate the current branch, and the red font indicates remote branches (branches on GitHub, in our case).

Changing Branches

To move from one branch to another, use “git checkout [branch name]“. So, to move to the “dev” branch created above, we can use “git checkout dev“.

To create a branch and move to it in a single command, use “git checkout -b [branch name]“.

Pushing a Branch to a Remote Repository

To push a newly created branch to a remote repository (for us, on GitHub), first make sure your current branch is the one you want to push and then use “git push -u origin [branch name]“. For example, to push our dev branch to GitHub, we can use “git checkout dev” (if not already in the dev branch) followed by “git push -u origin dev“.

Now, when listing all branches, you should see the dev branch added as a remote branch (red font):

You should also see the dev branch added to the list of branches on GitHub:

Now, let’s make a change to the code in our dev branch. I’m just going to move the std::cout over to a separate function:

#include <iostream>

void printTestMessage() {
    std::cout << "Testing...\n";
}

int main() {
    printTestMessage();
    return 0;
}

Now, we can add our changes to the dev branch by saving the file and then using the commands “git add -A” and “git commit -m "Move printing to separate function"“. To send these changes over to our remote dev branch, we can use “git push“.

Now you should see the dev branch code (but not the master branch code) updated in GitHub:

Merging Branches

If we’re satisfied with a change made to one branch and want to add those changes to another branch, we can “merge” the two branches together.

To do so, first go to the branch you want to add the changes to. I’ll be adding them to the master branch, so I need to go to that branch using “git checkout master“. Then, use the command “git merge [branch name]“, where “[branch name]” is the name of the branch with new changes. For merging the dev branch into the master branch, type “git merge dev“.

After doing so, any changes previously made in the dev branch will be made to the master branch. To update the remote master branch with these modifications, just use “git push“.

Pulling a Remote Branch

When sharing code through GitHub, different people may make changes to different branches over time. This can make managing branches a bit more complex.

Whenever a change is made to a remote branch by someone else, it’s always good to “pull” that branch over to your local repository. This way, the code on your computer will always be the most updated version.

To pull code from a remote branch, first checkout into the local branch you want to pull into, and then use “git pull“. For example, if someone were to modify the remote dev branch, we can add those changes to our local dev branch by using “git checkout dev” followed by “git pull“.

Resolving Merge Conflicts

When pulling code into your local repository, conflicts can arise. Let’s say someone modifies the master branch of the remote repository and you pull that into the master branch of your local repository. To simulate this, let’s modify the master branch a bit:

#include <iostream>

void printTestMessage() {
    std::cout << "Hello world!\n";
}

int main() {
    printTestMessage();
    return 0;
}

(Be sure to save the file, add it to your repository, and commit it.)

At the same time, let’s say you also changed “Testing…\n” in the dev branch to “Test!\n”:

#include <iostream>

void printTestMessage() {
    std::cout << "Test!\n";
}

int main() {
    printTestMessage();
    return 0;
}

(To make this change be sure to first checkout to the dev branch, then make the change, save the file, add it to your repository, and commit it.)

Now, if we want to merge our dev branch into our master branch, there’s a problem: both branches have a change made to the same line of code. This creates a “merge conflict.” Checkout to the master branch and use “git merge dev” to see what this looks like.

When a merge conflict occurs, the file with the conflict is updated to look something like this:

What this means is that the “HEAD” branch (master, in this case) contains the code above the “=======”, while the dev branch contains the code below. It’s our job to choose which code to accept into the master branch (since the computer doesn’t know how to choose here).

Te resolve our merge conflict, we just need to delete anything we don’t want to keep. This means that, to modify master with the change made in the dev branch, we can change the main.cpp file to look like this:

#include <iostream>

void printTestMessage() {
    std::cout << "Test!\n";
}

int main() {
    printTestMessage();
    return 0;
}

(Visual Studio Code also provides four buttons for possible actions to take. To do the same thing as above, just click the “Accept Incoming Change” button.)

After resolving the conflict, save the file, add it to your repository, and commit it. The commit message I typically use when resolving merge conflicts is usually just “Resolve merge conflict”, but it can be anything. Then, the conflict is fully resolved!

Go ahead and push the master and dev branches over to the remote repository now, to sync it up with all the changes we just made.

Creating a Local Repository from an Existing Remote Repository

Above, we created a local repository, added code to it, and then pushed that code to a remote repository. Oftentimes, however, we’ll want to do the opposite. That is, create a local repository based off of a remote repository that already exists and contains code within it.

To demonstrate this, let’s first create a new folder (can be named anything and located anywhere on your computer). Open up the folder, and right click anywhere inside of it. Then press “Git Bash Here,” like we did earlier.

We can add all code branches from a remote repository into our local repository using the “git clone [URL]” command, where “[URL]” is the URL of the remote repository. To add our test remote repository from GitHub (created earlier), go to the repository page and use the URL found here:

For me, I need to use the command “git clone https://github.com/MatthewATaylor/Test.git“.

Now, you should see a new folder created with all of the code from GitHub inside of it. To enter that folder from the Git command line, type “cd [folder name]“. The folder created for me is called “Test”, so I can enter that folder using “cd Test“.

Once inside of the folder, we can use any of the Git commands from before (since we’re now inside of a Git repository). Use “git branch -a” to get a list of all the branches cloned onto your computer:

You may notice that no local dev branch was created, so how can we do that? Simply type “git checkout dev” to create a local version of the remote dev branch and automatically checkout to it.

Common Practices

Most of the time when programming our robot, we’ll use a master branch for code that has been fully tested to work properly, and we’ll make most modifications in a dev branch. This will ensure that we never accidentally break perfectly functioning code.

Also, only a few people will likely have permissions to directly modify code in the master branch. This ensures that the master branch is only ever modified when the modifications have been tested to work. To request that changes be merged into the master branch, you’ll most often use “pull requests.”

Pull Requests

Pull requests allow people to request a merge into a branch they cannot directly modify (due to a lack of proper permissions). To make a pull request, go to the repository page in GitHub and click the “Pull requests” button from the top navigation bar:

Once on the pull requests page, click the green “New pull requests” button (top right). Then, on the next page, you can select which branch you want to merge into another branch. Below, I choose to request merging dev into master:

If the code in the two branches is identical, you won’t be able to create a pull request, so feel free to make a small change to the dev branch for the pull request to work.

Now (as long as the branches are different), you can press the green “Create pull request” button. This will prompt you to create a title and comment (optional) for the pull request. Just be sure to clearly describe what changes you made.

Once you finish creating your request, you’re able to accept it in this case (since you have permissions to merge with the master branch). However, if you can’t, a manager of the repository will likely review the pull request and decide for himself or herself whether or not to accept it.