Lesson 13: Git and Version Control
We are going to switch gears for this lesson and learn about something not directly related to FRC: version control. Version control is a very important component of the programming process. We are going to be learning a type of version control called git. Verify that you have git installed, if not install the git command line from the official git website.
Git solves a very big problem. Image you are working on a very document. You may save different versions of the file, such as "Document Apr 2015", "Document Backup", or "Document_updated". Without realizing, you just created your own version control system. This type of file naming will work for documents, but not for software projects. For software, we use a version control system, in our case, git. Git, and version controll in general, can do many things:
- Backup and restore. Revert to previous versions, if necessary.
- Synchronization. Several people can work on the same project at once.
- Undo. Sometimes we just want to get rid of some recent changes, or sometimes we mess up badly and need get rid of a weeks worth of work. Git can do this.
- Tracking Changes. We can see how a file was changed, and by who.
- Branching and merging. Git allows up to isolate some work that we need to get done before it should be added to the main project.
Hopefully you are starting to see the benefits. Git is essentially the "Document Apr 2015" for code. Git is very powerful, but unfortunately has a pretty steep learning curve. Everyone, including myself, cannot remember every little detail of git. If at any point you forget something or need to learn a command in greater depth than is described here, I recommend checking out the fantastic Git Reference site put together by the GitHub team.
OK, lets get down and dirty. Before we learn any commands, check out this high level overview of the git workflow:
- Working Directory: Make changes to your project.
- Staging Area: Move changes to the staging area
- Repository: Commit these changes to a repository.
Initializing a Repository
You can create an empty git repository in any directory by running this command:
Make some changes to your working directory. Add some files, change existing ones, delete some. When you are done, you need to move the changes that you want to keep into the staging area. When you stage a chage, it will be included in the next commit. Unstaged changes are not removed, they are just not included in the commit. You stage changes with the git add command. There a couple of different ways to use it:
git add --all
Adds all current changes to the staging area.
git add <file_path>
Adds only the specified file to the staging area. You can also chain several files together like this:
git add file1.txt file2.txt info.txt
You can also add whole directories:
git add dir1/
Once you have staged the changes that you want to keep, you need to commit them. This is how you commit:
git commit -m "<message>"
There are other ways to commit, but this is the most used command. It is important the your commit message describes what changes were made. In the context of FRC, you will often need to change or view your previous year's code in some way which becomes very difficult or impossible if your commit messages are not written well. An example of a good commit message could be: "Added a single controller mode to the KeyMap object." Usuallly this is descriptive enough, but if a commit contains a lot of changes, the commit message probably should contain more detail. When in doubt, err on the side of caution and add more detail.
Say you change a file and haven't commited it yet. You decide that how it was before was better. You can undo those modifications by running:
git checkout HEAD <filename>
This will change filename back to how it was before. HEAD simply means the last commit. Now pretend that you have accidentally staged something for commit that you don't want to commit. You can unstage it by running this command:
git reset HEAD <filename>
There is one more type of backtracking that you should know about: removing previous commits. You can see all of your previous commits by running the log command:
The random string of characters attached to each commit is called a SHA. You
will need to use this SHA when attempting to reset to a previous version. To
make it easier, git only requires that you use the first seven characters of the
SHA when reffering to commits. So if the SHA is
5d692065cf51a2f50ea8e7b19b5a7ae512f633ba you only need to use
5d69206. Anyway, to reset to a previous commit use this command:
git reset SHA
this will complete remove all commits that occured after the commit you specifiy. These commits are completely wiped out of history. This is useful if you make some big mistakes that you don't really want to keep.
If you are working on a big change that should not be put in the production
environment until it has been tested and ready, you can create a branch. You
have actually been committing to a branch already. Right now, your repository
only has one branch: the default branch, which is named
You can see what branch you are on by typing:
You can create a new branch by typing:
git branch <new_branch_name>
Branch names cannot contain spaces. Once you have created a branch, you can switch between branches by typing
git checkout <branch_name>
The checkout command will switch all the files in your working directory based on which branch you are currently on. Also, commits you make go to the branch that you are on. So if you create a branch named new_feature and commit changes to that branch, the master branch will stay preserved and not be edited. Once you are done with the new_feature branch, you can then merge it into the master branch using the merge command.
git merge <branch_name>
The merge command will merge <branch_name> into the current branch. So if you wanted to merge your new_feature into master, you would need to first checkout master, then run git merge new_feature.
Git will automatically combine changes in different branches if it can. But let's say you make different changes to the same line in two different branches. Git does not know which change you want to keep. In this case, git will create a merge conflict, which you will need to resolve. For example, you could have a file that looks like this:
You will need to remove all of git's markings, keep which the line the should be kept (or make a combination of the two) and then commit the result. You should use a commit message such as "resolved merge conflict."
When you are done working with a branch, you can delete it with:
git branch -d <branch_name>
If you are working on a project with other people, you will want a remote. Code Red uses GitHub, a popular remote service. After you setup a repository on GitHub's website, you need to tell your local repository where it is:
git remote add <name> <location>
git remote add origin https://github.com/CodeRed2771/2016RobotCode.git
You can list all the remotes for your repository with this command:
git remote -v
If you want to get a already existing remote repository to your machine (such as one created by a team member), use the clone command:
git clone <location>
The clone command will setup a remote with the name origin.
Once your remote is setup, you can use the following commands to "push" your changes to it:
git push <remote_name> <branch_name>
"pushes" any changes in your local repository to the remote.
Gets the changes from the remote but does not merge them into your local repository. Remote branches are stored on your machine in a way similar to branches. So the remote's master branch would be origin/master. You could merge the remote repository into your local repository by checking out the master branch and running:
git merge origin/master
To combine the last two commands into one, you could use the pull command, which will both fetch and merge the remote:
If you are getting an error when trying to push code to a remote saying that the push is "non-fast-forward", this means that the remote has commits that your local repository does not. You will need to pull the changes from the remote, resolve conflicts if necessary, and then push the result.
Hopefully you have a general understanding of how you use git. I realize git is hard, so if you don't fully understand what you just read, ask a team member who does understand, or go over to codecademy.com, setup an account and take their git tutorial. It should take two hours max, probably less.