
Software development happens over time. Software development happens collaboratively. In order to see, manage, and build on previous iterations of software, source control systems have been developed to assist in this process.
In this course I am going to teach you todays most populare source control system by far--Git. This course introduces Git so that you understand the foundations of Git. This is not a course where you are going to memorize git commands, but a course where you are going to understand the foundations of how source control systems work, so you can have the mental model to use Git effectively for a long term and as the system evolves.
Let's get started!
If there's one thing to change how you think of Git in this course, think of it as a file system.
So if you've used the command line previously, and are comfortable with navigating files, then you should be able to understand Git without a problem.
But it perhaps may also be comforting to know that Git is just a file system, and behind the scenes, there is no magic going on--just blobs of data organized in a tree.
My expectation is that you know a little bit of command line and can otherwise navigate the terminal.
If you are not otherwise comfortable navigating the terminal, please refer to the first few lessons of my course "Introduction to the Terminal: Linux, Mac, and Windows" linked in the resources section.
Let's discuss our learning objectives!
Students will learn what source control (sometimes called version control) is
Students will learn arguably the most popular source control system--'git' on the terminal
Students will learn the most common features of git to be productive for their first internship, hobby project, startup, or regular work.
Students will learn about the internals of how git works (not typically talked about in tutorials)
Students will get hands on experience, and learn how to become an advanced git user by understanding and developing a workflow with git.
Folks who have been using computers a while have likely experienced losing data. When I was writing one of my first video games, I lost all of my source code due to a power outage. No backups were automatically made, no secret backup files, no auto-save. This only has to happen to you once before you decide to use a system like git to save and backup your work frequently, or even automatically.
Not only did I lose my code, but I also lost the history of 'why' I did things as I did (r.i.p comments....)
Git is not the only version control software. Git was created around 2005 by Linus Torvalds specifically to help with a larger project--the linux kernel to manage contributions to a code base by many developers. However, it's not the only system that exists out there--and it's worth noting others exist.
Git itself is the most popular today and excellent and best at managing source or text-based assets.
Concurrent Version Systems (a.k.a CVS) was popular earlier on in the 90s to early 2000s.
https://en.wikipedia.org/wiki/Concurrent_Versions_System
Subversion (a.k.a SVN) overtook CVS for the most part, and now Git has largely overtaken SVN. We'll learn the difference as SVN relies on a 'central repository' versus having 'local repositories' like Git.
Perforce is another popular version control system, and it's largely used in multimedia and gaming companies because it handles 'binary' assets (e.g. executable files, 3D assets, data files, etc.) in a more space efficient matter.
https://www.perforce.com/blog/vcs/what-source-control
Typically it will be enough to run: sudo apt-get install git
You can otherwise refer to your package manager of choice while following the git install page here: https://git-scm.com/download/linux
Most Mac users who have already installed XCode should have 'Git' installed already. You can open a terminal and type 'git --version' to check.
Otherwise, check out https://git-scm.com/download/mac for installation options. Personally, I like to use 'brew' to install git.
Most Windows users likely do not have Git installed by default. For this series, download the git command line tools from:
Download the latest version from: https://git-scm.com/download/win
Alternatively you can use the Windows Linux Subsystem
Once you've installed git for your respective operating system, you can verify on the command-line that it has been installed with the following commands.
git --version
Retrieve git version
git config --list or git config --list --name-only
List all of your configuration options
git config user.email
See a specific configuration option
Note, in this series, we are not going to cover Graphical User Interface (GUI) tools. The reasons are:
The command-line is much more powerful (I don't know of any GUI's that have a button that does 'everything' you can do on the command line)
The command-line allows for easier automation of common tasks
You can always default back to a GUI once you understand the fundamentals, so you know what is going on behind the scenes.
Git is the most widely used modern version control system. Typically it is used to manage different versions of text-based files (though it can manage any set of data.
Git again is a version control system. More specifically however, it is a distributed version control system (DVCS). This means that every time you are cloning (whether that means you and your teammates, or you multiple times), you have the repository with the entire history. Version control software (like git) helps you learn how to develop software.
Keep in mind, because the system is 'distributed' things in git work a little differently, and that's why we want to build an intuition in this course.
In this lesson we will be working 'locally' without any internet connection needed. This is important to keep in mind--most of the operations you are going to be doing are done locally.
(NOTE To self: Use 'git status' at the end to lead into the next lesson talking about tracked versus untracked files)
In this lesson I will show you how to create a local repository on your machine. This is the first step to setting up your codebase to use git and to track files in your version control system.
git init or git init repository_name
Initializes a git repository on your local system
git -lah
This will show the hidden '.git' folder (files and folders that start with . are typically hidden) to confirm that you have created a local repository.
Note--you should never need to navigate to this folder.
Git configuration options
git --version
Retrieve git version
git config --list
List all of your configuration options
git config user.email
See a specific configuration option
git status
A command to show the status of our git repository at any time.
In this lesson we learn about two fundamental commands, git add, and git commit.
git add
Tells us that we want to track this file and start tracking it.
We are also 'staging' this file (we can think of it like a rough draft) to be later committed.
git commit -m "message"
Once we have 'added' files that we want to track, we 'commit' them.
The 'commit' command is what creates a snapshot of our program.
Note: That we always add a useful message to describe what the changes are and why they were made--that's the '-m "message"' portion of the commit, known as the commit message.
Shortcut to add and commit a file
git commit -am "descriptive message about your commit"
Remember, Git is a version control system. It's job is to keep track of different versions of files, or you can think of different 'snapshots' as you develop your software project. In order for git to know which files to keep track of, and which snapshots to take, we need to understand a little bit of the basic workflow.
untracked files
git sees that these files 'exist' but will do nothing with. Perhaps they are just temporary files for example.
tracked files
These are files that 'git' will keep track of, meaning we want to be able to see the history of changes for that file.
staged (usually in a green color in the terminal)
These are the tracked files that when we do a 'git commit' will be saved to the snapshot.
unstaged (usually in a red color in the terminal)
These are the files that we are tracking, but we are not taking a 'snapshot' of them to be updated in the latest snapshot.
The git status is a command that you will be using frequently. It tells you a few important pieces of information about your 'working tree'. The first piece is which branch you are on (by default the 'main' or 'master' branch) which we will discuss later.
The second portion is about the current status about files that you are getting ready to commit with git add and git commit. This means the status of files are that are untracked and modified files (git status -s shows an abbreviated form of this.)
Changes to be committed
Files that you have previously performed the 'git add' command with.
Changes not staged for commit
Tracked files - Files that are being tracked, and have been modified but are not staged for committing.
Untracked files
Files that git sees that exist but 'git' will not manage until you perform a 'git add'
Remember what git is, it's a version control system that allows us to track changes to files. So in order to see those changes, we can see a log of your commits.
git log
Shows a listing of our commit messages.
git log filename
Show a listing of commit messages for that particular file.
git log -p filename
This is also useful for outputting a specific file.
There are many other variations of how the log is displayed as well.
git log --oneline
This is a nice way to view the commit messages in a compact form. Very useful when browsing rapidly changing or more unfamiliar git repositories.
git log -- stat
Show statistics for each file modified.
git log --pretty
Alternative way to print out
Note: This requires various 'format identifiers' but allows you to customize your output.
git log -- graph
ASCII graph of branches and merges
Additional command of interest.
git reflog
Is another way to list commits, and also perform other operations and queries.
This will also show a 'history' of 'actions' that have occurred locally on your repository, beyond the commit history.
A best practice and from personal experience that you should add and commit often. This allows you to aggressively try to make changes, and for teammates to follow your changes more easily.
Make your commit messages specific to the issues that were fixed. I recommend addressing at least three things:
What you fixed
Why you fixed it
How you fixed it
You can create templates as needed to help you fill out this information quickly, and in some case folks will simply link 'jira ticket numbers' where this information may be tracked elsewhere and viewable by project managers.
Eventually you'll be working 'upstream' (in later lessons) and it means other folks are going to see your changes, and it's much easier for them to see small incremental changes versus large infrequent ones should they need to understand 'why' something was done.
Note: Here is the useful git log command we used
git log -p filename
This is also useful for outputting a specific file.
It can be helpful as a new learner to see what is going on in git. There is a tool called 'gitk' that can be installed. I'll show you the Linux tool now so you can see.
Install with ‘sudo apt install gitk’ if you do not have it on linux.
This tool is completely optional, but perhaps it will open you to explore other cool tools that help further your understanding of git.
You may have learned on the terminal that you can move files using the mv command. Git needs a little bit more help however when moving files around (if you choose to reorganize your repository), so that the version history will also move with the file.
git mv file_from file_to
Git command to use when moving files around in the repository.
Moves the file, and also takes care of renaming the files behind the scenes for you.
So just as there is a git add command, there is a way to removal files from your staging area using git rm. This will also remove the file from your hard drive after you commit, because you are telling git to no longer track this file, and your latest snapshot will not have it (however, previous commits will still have that file).
git rm some_file
Removes the file from your git repository and your file system (i.e. deleting it from your file system--again, the old file will live in a previous git commit however)
Note: If you do want to keep the file on your hard drive, but not have it deleted, use: git rm --cached whatever_the_file_name_is. This will keep the file, but not commit it in your repository. An example of when you might do this is if you have a binary file that took a long time to compile, but you do not want to commit it to your repository.
When you create a repository a hidden directory is located within the .git directory. This is typically hidden because you as a user do not (nor should) modify the files and directories.
However, being able to see and think of gif as a file system can be helpful to understanding what 'git' is actually doing when you are issuing commands.
In this lesson I am going to show you how to 'watch' what is going on in the internal .git directory.
watch -n 1 tree .git
Shows all the changes that are happening as we make changes within our .git directory every 1 second.
watch -n 1 tree .
Similar to above, but this time we're just seeing all of our files.
This can be helpful when you perform a 'git checkout' if you want to see how things are changing at a high level.
Note: You may need to install the 'tree' command within your terminal to see the changes. This lesson is not essential, but may be useful again to understand that git is file system.
There is no 'git cp' that is natively built into git. The reason being is that in order to copy the file, we'd also have to copy all of the commit history associated with that file. It's not that it would be too hard to implement, but rather it's not always clear that's what the programmer would want.
Thus, instead we simply do a regular 'cp' and then a 'git add' for files that we want to track manually.
Again, to get an intuition as to why this is working, we have to recall that git is a filesystem.
The built-in help system is quite powerful and can be utilized to get documentation about various commands.
e.g.
git help commit
As we learn commands in git, you can use this system to query for help at any time, regardless if you have an internet connection or not.
'diff' is a tool available in most terminals to see what is different among two different files. Similarly in git we can 'git diff' between two versions of a file in different commits.
git diff filename
Tells the differences between the current file and the last commit.
Some helpful options with git diff
git diff --color filename
Colors the diffs when available.
In this section, I am going to show you how to rewind your work. This is actually the point of a source control system, the ability to iterate, try new ideas, and not have a fear of losing your work!
In this lesson I explain a few of the concepts in git and the terms used.
Note: "git help glossary" will bring up many of these terms.
Main branch (note: In some tutorials you may see this referred to as master branch. 'main' is a clearer term and has been renamed for historical reasons)
HEAD (latest revision commit where you are currently at)
This is the pointer to where you are currently working.
(head -- with lowercase) is the revision for a given branch
(detached head means you're looking at an older revision in the branch, and you need to move to the latest revision to do new work).
git reset is the fundamental command that restores the state of your local repository. So it lets you work backwards. Combined with the 'git log' command, you can look through your commit history and use git reset to 'rewind' to a previous snapshot of your codebase.
git reset specific_file
Unstages the file for the current commit
git reset --hard
Use this if you have modified files and just want to go back to your last commit.
git reset HEAD~2
This moves the current branch back '2' commits.
Note: The tilde (~) mark, as that will be used in other commands to indicate moving back.
git reset --hard
The 'hard' flag states that you don't care about any previous changes you have made, and that you really want to revert to your previous commit.
git reset --hard HEAD~1
Moves us back one commit from where we currently are (i.e. to our previous commit)
git reset --hard HEAD@{1}
This will do the same thing
The git checkout command allows us to do two main things. 1.) Restore working tree files (i.e. work tree is the branch (a directory behind the scenes of files) you are working on and 2.) Switch branches.
In this lesson, we'll cover retrieving files.
git checkout main.c
Revert back to the latest commit
git checkout master
Revert all the files back to the latest commit
git checkout main~2 main.c
Revert back to the 'main.c' file from two previous commits.
git checkout hash_value main.c
Revert back to a specific version of main.c
In this lesson we are going to learn how to work with 'branches' which allow us to create, test, and develop features in a branch off of the 'main' development branch.
A branch is useful because it allows developers to not have as many merge conflicts, whilst also keeping the main development branch clean until a feature is ready to be released.
git branch -a or git branch (no arguments)
Shows all the branches and which you are working on.
You may notice we do not have a 'refactor' branch yet.
git branch feature
Let's create a separate branch 'feature' to work on.
git branch -a
Show branches we have to confirm it was made
The git checkout command allows us to do two main things. 1.) Restore working tree files (i.e. work tree is the branch (a directory behind the scenes of files) you are working on and 2.) Switch branches.
In this lesson, we'll cover switching branches
git checkout main
Go back to the main branch
git checkout main~2 main.c
Revert back to the 'main.c' file from two previous commits.
git checkout 'feature'
In this lesson we see how to merge the branch that we have created into the main working branch.
This is a common practice when working together on software where you want to merge in features
Switch to the branch you want to merge into (e.g. master)
git merge branch_name
What is a merge conflict? A merge conflict occurs when two programmers are working on the same file, and the contents are not able to be distinguished (e.g. the same line in the same function of two files contains different text).
Programmers thus have to manually fix the merge conflict before they are able to commit by inspecting the code.
How often do merge conflicts happen? Well, depending on how many programmers are working on the files, it can happen very often. Git provides us some tools like 'branches' to help allow development to proceed, but eventually that content will get merged. Thus, one of the ways to avoid merge conflicts may be to consider divided very large files, into smaller files with less responsibilities so developers avoid as frequently modifying the same files.
Consider this your midterm, but do not worry you're not being assigned a grade. At this point, you should be relatively comfortable with some of the commands, so let's practice recalling them in a quiz.
Now we are going to talk about how to push our code 'upstream'. Upstream means to a remote host--or a website like github.com. We are pushing code 'up' to the cloud.
Note: Sometimes you may hear the term 'downstream' which means the opposite, and this is when we are cloning a repository from a remote site (i.e. github.com) to your local machine.
Keep in mind that 'Git' is not the same thing as 'Github'. Github is one of many hosts for git repositories. Github.com ('Github' for short) is the webhost for the software version control system called 'git'.
A few popular websites where you may host git projects are:
Github
Gitlab
bitbucket
Sourgeforge
Any will be fine for the majority of this course. Note that you will need to create a free account with each of these hosts.
We have been working primarily locally, and this has been a valid way to work. However, most of us will want to collaborate with others online and host our files on the internet. In git lingo, this is called a 'remote', which is just some other network location (i.e. some server on the internet).
git init
Create a new local repository
Create a repository on github.com
git remote add origin https://git.some.url.com/username/project-name.git
Set our repository to the 'upstream
Note that we could have
Some other useful commands
git remote
This will list the ste of 'repositories' (i.e. remotes) whose branches you are tracking.
git remote get-url --all origin
This will retrieve the github url (which is probably what you are interested in)
When you use the 'git clone' command this will establish a link between the 'upstreamed' remote repository that is hosted on github.com to a local repository. You can now develop on your local environment using all of the github commands that we have learned. Then when you are ready, you can 'push' your changes upstream, somewhere that will be accessible to you no matter which computer you use with your entire commit history.
The 'git push' command uploads your commit from your local repository to a remote repository (i.e. one that is 'upstream') located on some other remote (i.e. a server on the internet hosting your github repository).
Pragmatically, I like teaching folks to always do three things:
git add, git commit, git push.
By doing this three commands frequently, you will avoid losing your data and always have backups available.
'git pull' we can think of as the opposite of 'git push'. With 'git push' we are sending (i.e. merging--which we'll talk about later) to a remote repository our commits with our staged files.
So 'git pull' will grab the latest changes from our remote (i.e. upstream at someplace like github.com) and merge them into our local repository where we can continue development.
git pull - Fetch and integrate with another repository on our local branch.
'git fetch' will bring in changes, but will not report any merge conflicts if there are any. This is almost the same as git pull, but it will avoid any merge conflicts. You will later have to run 'git merge' and then resolve the merge conflicts.
We have looked already at creating new branches. But we can also push branches upstream to a host like github. In this lesson, we will undersatnd how to push a new branch upstream.
git push -u origin refactor
Now we are going to push your changes 'upstream' in
a separate branch. The '-u' argument sets us up to push changes upstream, or otherwise to be tracked by github.com. This means our branch that has only been created on our 'local' machine, will now be accessible on a remote machine upstream (i.e. a 'remote' or some 'server' in the internet).
Why might we want to do this? This way other developers on a team project can see different feature or developer branches online as we make progress.
In lesson we go through a pull request of a branch for a feature that we have created.
A .gitignore file is a special file that can ignore specific files, or specific patterns of files to not track.
A specific use case would be to ignore temporary files that are generated by your executable when running the program. Additionally you may want to omit binary file types (e.g. .exe, .so, .o, .dll, .dylib, etc.) that are not very efficient to store in git repositories upstream.
Prepare for a second review quiz!
Congratulations on completing this course--it was no small feat! Now that you have finished this course--go out to the real world and apply your skills!
Revisit the "a working example of Git" at the start of this course to review all that you have learned.
Start a hobby project and use git to reinforce the skills and practices that you have learned.
Look at other features of github, gitlab, or whatever your chosen host is to continue building your workflow.
Congratulations again, and be sure to check out any 'Going Further Lessons' we add at the end in the future!
The following are some brief notes and articles that may be of interest for going further in the course. Some of these commands are self explanatory and just need a try. Others may be useful just so you know what is possible with Git!
Very frequently I'll go through the work of doing a 'git add, git commit' cycle and forget to add a file. Luckily there is a command to fix that--git --amend. This will create a single commit with the forgotten file.
git commit -m 'Your informative message'
git add forgotten_file
git commit --amend
This adds the forgotten file and makes a single commit.
The 'git grep' command is quite handy, in that it searches not only the current files, but it searches ALL of the commits, finding changes that may have occurred over time.
This is particularly useful if you are looking at an unfamiliar codebase.
It doesn't have to be accusatory, but it is helpful to see who the last person was who modified that piece of code.
You can run this command with 'git blame'
Similar to merge, but has the ability to rewrite the commits 'backwards' in times. Thus, we can change the 'base' of our branch.
Example usage
git rebase - i HEAD~3
This would allow us to go through and for example remove commits from our history, 3 commits prior.
Recall that git is a file system, and the file system is maintained in the .git direcotry.
Removing the git repository then is as simple as:
rm -rf .git
Here are a few notes about the internals of Git
Internally git is using a ‘hash tree’ or ‘merkle tree’
Under the hood, git does duplicate things, this is why we do not typically store binary files in git.
This is interesting because we can do operations to quickly check out different projects since we have a reference to the right commit with all for the files we need.
Other source control systems like subversion for instance may do ‘diffs’ on files, which save on storage. This may be useful if you’re storing very large binary files.
Every object has a hash.
How do we reliably give objects a unique identifier? We use the sha hash to get a unique identifier of the data. Sha hash gives a deterministic way to do this.
git show is useful for further inspecting these 4 objects in Git.
Blob
Tree
Commit
Annotated tag
Git LFS (Large File Storage) is one way to store files that may be larger (perhaps binaries, or large multimedia assets).
Typically other tools may be used, but it is otherwise possible to extend git to store files.
Overview
In this course you will learn the most popular source control tool--Git. Beyond learning the foundations of Git, we are also going to build your knowledge on the foundations of Git, so you understand how it works behind the scenes. This foundational knowledge will give you confidence to continue learning and using Git, whether for hobby projects or on the job. No matter what level you are in your career, learning Git will help you work smarter and more iteratively on your programming projects. This course will start with the fundamentals learning git commands locally, and then we will apply those skills to repositories hosted remotely. This is the Git crash course that you can complete start to finish in a weekend.
Topics you'll learn
Students should take this course if they want to learn:
How to use setup Git for your respective operating system.
How to use the basic git commands: init, add, commit, push, pull, merge, branch
How to resolve scenarios like the dreaded merge conflict!
Why you should take this course?
This course is accessible for students who are self-taught, in a boot camp, or university students and want to learn an essential skill. As an instructor I will work you through concepts at a relatively slow and gentle pace, but I do not hide the details from you of how Git works. This course can be your one stop shop for understanding source control management, and help you confidently use source control on the job.
This course is also compact, meaning you can complete it in a weekend as opposed to massive courses that are left unfinished and leave you wondering if you have big gaps in your knowledge when you revisit them. In this course, we learn a skill and then practice that skill to reinforce the learning in a practical scenario. I work through solutions step by step with the reason 'why' not just 'what to type'.
Who am I?
I have been teaching for over 10 years in universities and as a professor. I have worked in industry in big companies, startups, and as a consultant. I am looking forward to being your instructor for this course, and I hope you will get great value out of the lessons learned!