Git is a commonly used software that keeps track of changes made to files and directories. It excels at keeping track of text changes, which is all source code is. You likely already follow a process similar to how Git works. If you have a text document, then you make some changes to it. The first draft so to speak is version 1. The second draft is version 2, and so on. Git keeps track of these types of changes over time. It also gives you a type of redo and undo. With Git you can move to any point in the timeline. Git is a version control system and is the most common one used. It came to be from a need of Programmers wanting a way to be able to track the changes that they made to computer code over time, as they added features and as they fixed bugs. This type of software is also known as Source Code Management.
The History of Git
Back in 1972, there was a tool called Source Code Control System, or SCCS, released by AT&T. It was used on the Unix operating system and became popular in Universities and various research projects. SCCS was the version control tool to use right until the early 80s. That is when RCS, or Revision Control System, was released. RCS became popular since it was cross-platform, whereas SCCS was Unix only. It had easier to learn syntax, more features and was faster than SCCS. SCCS and RCS had a shortcoming in that they only allowed you to work with one file at a time. This allowed you to track changes in a single file, but not in sets of files or in a whole project. Along came Concurrent Versions System which made it possible to work with multiple files and the idea of a code repository. This started the concept of putting that code on a remote server where more than one user could work on the same file at the same time. The next innovation came with Apache Subversion or SVN for short. SVN added the ability to save non-text files as well as improved tracking and commit process. Bitkeeper was another version control system which worked well but became a paid only software. In 2005 Git was created by Linus Torvalds, the creator of Linux. Git went on to become the number 1 version control tool and remains so to this day. In 2008, GitHub launched its service that allowed anyone to host Git source code repositories.
Git can be installed on Windows, Mac, and Linux. You can download Git here if you do not already have it. In this tutorial, we will be running Git on Windows. When running the installer, just accept all the defaults as there are a lot of options.
Getting Started With Git
To get started with Git, we can create an example project folder that will hold some files we are going to track using Git. Here we navigate to that directory and run the git –version command to verify Git is installed and running.
Git is going to bark at you if you do not add a user name and email to the configuration, so we can just add some example data to get started using git config –global user.name “gituser” and git config –global user.email “email@example.com”.
Auto Complete and Help
Auto complete in Git is a nice feature that helps you type commands, file names, branch names, and other things to make the workflow easier. So you can start to type a word, then use the Tab key to see have Git try to complete the rest of the command you might have had in mind. It helps save a little typing. In the case that there are several options available, Git will notify you it needs more info or provides your options. For example, if we type git a, then press the tab key, the possible completions are add, am, apply, archive, and askyesorno.
By typing git help, you can view a list of commonly used commands in Git that are divided into 5 categories.
These are common Git commands used in various situations:
1. start a working area
- clone Clone a repository into a new directory
- init Create an empty Git repository or reinitialize an existing one
2. work on the current change
- add Add file contents to the index
- mv Move or rename a file, a directory, or a symlink
- restore Restore working tree files
- rm Remove files from the working tree and from the index
- sparse-checkout Initialize and modify the sparse-checkout
3. examine the history and state
- bisect Use binary search to find the commit that introduced a bug
- diff Show changes between commits, commit and working tree, etc
- grep Print lines matching a pattern
- log Show commit logs
- show Show various types of objects
- status Show the working tree status
4. grow, mark and tweak your common history
- branch List, create, or delete branches
- commit Record changes to the repository
- merge Join two or more development histories together
- rebase Reapply commits on top of another base tip
- reset Reset current HEAD to the specified state
- switch Switch branches
- tag Create, list, delete or verify a tag object signed with GPG
- fetch Download objects and refs from another repository
- pull Fetch from and integrate with another repository or a local branch
- push Update remote refs along with associated objects
To get detailed help on individual commands just type something like git help init, git help clone, git help add, etc..
Now we can start testing out how to use Git in a local environment.
Initialize A Repository
To start a repository, you need a directory to hold your project. We already have a folder named exampleproject which is where our project will live. You can initialize a repository with no files, some files, or many files. Git will know how to proceed accordingly. You can simply type git init to get started, and Git will let you know that it has initialized a Git repository.
The .git Folder
After you initialize a repository you’ll find a new folder of .git in your project root. It is a hidden folder so in order to see it you will need to use ls -la. This invisible Git directory is where Git is going to do all of the tracking of the project. Once this directory is present, we know that the Git repository has been initialized and is ready to go. The key takeaway is that this Git directory is all of Git’s tracking. If you were to delete this Git directory, Git is removed from the project. So to add Git tracking to a project, use git init. To remove Git tracking from a project, delete the .git folder.
The 3 Trees of Git
Git makes use of a Three tree structure. These are the Repository, the Staging Index, and the Working copy. As you’re working with Git, it helps to be aware of these different trees. The working directory contains changes that may not be tracked by Git yet. The Staging Index contains changes that we’re about to commit into the repository and the actual Repository which is fully tracked by Git.
Making A Commit
Git is now tracking our project. It’s not much of a project, because we have no files in it. Let’s change that by adding an index.html file now.
Let’s open our exampleproject folder in Visual Studio Code. VS Code is Git aware, so we will see something neat now. We see the index.html file in green, a letter U, and a number 1 on the left. Git sees this new file in the project.
A Git commit follows a three-step process.
- 1. Make a change
- 2. Add the change
- 3. Commit the change
We already have taken step 1. We added a file to the project. That is a change. It could also be a file edit or some other action. Let’s finish the commit with git add and git commit now. First, we do the add.
In Visual Studio Code, that change is picked up on. It sees that the file has been added.
Now we make the commit using git commit and use the -m flag to add a commit message. You always want to add a descriptive summary type of message to a commit.
In Visual Studio Code, we no longer see any special coloring or number indicators on the Git icon to the left. What was in the Staging Index is now committed, and VS Code is happy. This is the equivalent of using git status where we get the message of “nothing to commit, working tree clean”.
Some best practices for commit messages are as follows.
- Use a short single-line summary of fewer than 50 characters.
- You can optionally follow this with a blank line and a longer description.
- Each line should have fewer than 72 characters.
- Write the message in the present tense. “Fixes Typo” rather than “Fixed Typo”.
- Be clear and concise.
- To make bullet points you can use hyphens or asterisks.
- Optionally add tracking numbers like bug id or support ticket id.
View The Git Log
Every change is tracked in Git and we can view those changes using the log. In the project root, type git log to see the log of commits. We have only made one commit, but if there were several, the newest entry would be at the top.
In the above image, we can see the text of (HEAD -> master) next to the commit. What is this? This is the head pointer in Git. It is a reference variable in the Git system which points to a specific commit in the repository. Its key points are as follows.
- Pointer to tip of current branch in the repository.
- Last state of repository, what was last checked out.
- Points to the parent of next commit where writing takes place.
Adding Files To Our Repo
Our project is not much of a project with just one index.html file in it. Let’s add two more files of page_two.html and page_three.html. We’ll then try a new git command: git status.
We can see the two new files. The git status command tells us “nothing added to commit but untracked files present (use ‘git add’ to track)”. Visual Studio Code also sees the two new files, which are green and have a letter U indicating they are untracked.
Untracked vs Tracked
Just what does untracked and tracked mean? Well, the two files we just added are not being tracked. Git is aware they exist but does not care if they are changed or updated. The first file we created, however, is tracked, it has already been committed to the repository. All three files are empty. Let’s do a test and fill out some HTML markup in all three files then run a git status command to see what changed. The index.html file has a message of modified, with changes not yet staged for commit. In other words, the file in the working directory has changed since the last commit. The other two files are not listed as modified. They simply still have the same status of untracked files.
VS Code also lists the files in the same way. Index.html has an M for modified, and the other two files have the U for untracked.
This brings us to the concept of Atomic Commits. We want to add these changes to the repository, but they aren’t entirely related. We could add and commit them all at once, but it might be better to group them into related chunks. It’s the same idea of breaking up code into specific functions. It might make sense to add a commit for modifying the index.html file, then adding a commit to include the two additional pages to the repository. We can start with git add index.html and git status. The index.html file turns to green text with a message of “Changes to be committed”. The other two files still have the same untracked status.
In VS Code you can see staged changes by clicking on the Git icon and viewing the staged changes area. This matches the output of our git status command just above.
We can now commit the changes that are currently staged.
git commit -m "Add html markup to index file"
Let’s add and commit the two untracked files in one go. Here we will use git add ., to add all files to staging at once, then git status to view the changes to be committed, and then finally we can commit both files with git commit. I think you are starting to see the git workflow now!
We can run git log again to see all the commits so far. This should illustrate that Git is like an activity tracker for your code. It tells you what happened on each step of the lifecycle. We can see that the Initial Commit is at the bottom of the log output. That was our first commit where we simply added a blank index.html file. Each commit has a unique id which is a 40-character checksum hash. This is created via the SHA-1 algorithm. So in our case, e703e01c587d141308359c8fff53ec8af71773c0 was the first commit, f77c5907fb319c2b9a70c3e2fa73a860f0676bcb was the second commit, and 3eb9816d034f2338ca193e14bb80628ac8d4b1e3 is the most recent commit. As we learned, the HEAD pointer is pointing at the latest commit of the branch we are working on. Perfect!
View Changes With diff
So far we have used git status to view what state a file is in. We are able to see which of the three trees a file currently belongs to, if it has been modified, and so on. What if we want to be able to see what actually changed? Can that be done? Yes it can. You can view the changes of a file using git diff. We have three pages in the project so far. Let’s make a change to index.html, then we’ll use git diff to see what happened before committing anything.
When we call the diff program it shows the different versions of the files and compares them. The format seen above is the common format used. It’s comparing file A and file B. File A is the version that’s in the repository. It is the most up to date version of the file that we have committed. File B is the one that’s in the working directory. So from the output, we can easily see what has changed via the + sign and the colored green text. The working copy has a text link that the file in the repository does not have.
We can make another change to the file, and run git diff again to see the result. Interesting! Now we have both a – sign with red text and a + plus sign with green text. If something was removed from the file, this is shown with the red text and – sign. So now, instead of the H1 tag having the text of “This is the index page.”, it now has been changed to read “This is the home page.”. By using git diff, you can see anything that has been removed, added, or changed.
To drill down even further on the changes you can use git diff –word-diff. This shows us that we removed the word “index” and replaced with the word “home”.
Now that we know exactly what has changed we can make the commit if we like. As we have seen to make a commit we need to add the file to staging then make the commit. This is such a common workflow that Git provides a shortcut so you can add to staging and commit to the repository in one go. To do this use git -am “The commit message”. We use this technique below, and check that the working tree is clean using git status.
Delete A File In Git
Handling a delete in Git might feel a little strange since it is something you still need to commit to the repository. It might be best to just see a demonstration. First, we are going to add two new pages to the project. These will be added to the working tree and will at first be untracked by git.
Now if I delete one of these files right away, it will just disappear. Git isn’t yet tracking these files since they are not in our repository or in the staging tree yet, they’re only in the working directory right now. What happens if we add them to staging? Well let’s do that now.
Let’s commit those files as well.
We now have two new files in the master branch of the repository. To delete a file when using Git you can either do it manually or use Git to complete the delete for you.
For page_four.html, let’s just delete the file in VS Code.
The git status command shows us that Git knows about this file deletion in the working directory.
Here is a little mind-bender now. This delete needs to be “added” to staging so we can commit it. Well, the file is gone, so how do we add it to staging. We can move this delete to staging by using git rm like so.
So now that change is in staging. We can commit this change to the repository now.
The file is deleted, Git knows about it, the repository is updated, and the working tree is clean. Everybody is happy. The other option for a file delete in Git is to skip the manual deletion, and just tell Git to do the delete. This deletes the file from the working directory and stages the change in one shot.
In VS Code, that file is gone. We only have index.html, page_two.html, and page_three.html left in the working directory.
Lastly, we can commit that delete to the repository.
Finally, we can view the entire lifecycle of our git demonstration with git log. The output almost tells a story by just looking at each commit message. The first is at the bottom where we had an initial commit. Next, we added HTML markup to the index file in the second commit. In the third commit, pages two and three with HTML markup were added. The fourth commit saw some altered text and the addition of a hyperlink. We added two pages in commit five. In commit six, we deleted page four manually. In commit seven, we deleted page five using Git directly. Continue this process tens, hundreds, or thousands of times, and you have yourself a real-life software project! 🙂
Viewing A Specific Commit
Every commit is uniquely identified with that 40-character checksum SHA-1 hash that we learned about. We can use the hash of any particular commit to view the details of what changed in that exact commit. This is helpful if you need to come back at a later time, and you completely forgot when or why something was changed in the project. We use git show in combination with the commit id to view that commit. Here is an example.
Making A Multi-Line Commit Message
You might want to make a multi-line commit message and we have not seen that yet so let’s try it out now. We’ll make some changes to all three files in our project, then view a git status to see that indeed things have changed.
To make a multi-line commit we can issue the command of git commit -a while leaving the -m option off. Git still wants a commit message, but we did not specify in the command line. Let’s see what happens.
VS Code will now give you the ability to enter a multi-line commit message.
Once you save and close this file in your editor, the commit will process and your command line git session will update automatically. Cool!
Git provides ways to undo changes, even after you close your editor. This is a nice feature, as I’m sure you have had times when you needed to revert some code or text, but somehow lost all the changes. The undo ability applies to the working directory, staging index, and in some cases to actual commits. For example, say we were working on the index.html page and we accidentally deleted the footer, and also closed the file. Those changes are now gone… Or are they?! Let’s do a git status.
Notice the text that says, “(use “git restore
This worked. The index.html had changed, we removed the entire footer in the HTML, saved and closed the file. That means no ability to undo in the editor. Git saved us in this situation. By typing git restore index.html, those changes are rolled back and the footer is back in place when we open that file.
Git restore is fairly new. The same result can be had with git checkout as seen here.
What about if you already added a file to staging? In that case, you can use the same git restore command, but you can specify the –staged flag. In the following example, we make some text edits to page_two.html and then add the file to the staging index. Then we simply move it back out of the staging index into the working directory.
Every commit has a checksum that is an identifier as we learned about. The way this works is that when you go to make a commit, Git looks at all of the changes in the file as well as all metadata, runs everything through an algorithm, and assigns the unique id. If anything at all changes in the commit, the checksum breaks and the integrity of the commit chain fails. So in general, you don’t really edit older commits. The one commit you can change is the current commit. Editing the most recent commit has no bad side effects because it is the last in the chain. No other commit checksums depend on the current commit. As an example, we open up page_three.html and add a sub heading.
Running git status and git diff show us what has changed.
This looks good, so we commit the changes.
If you change your mind about that commit, you can amend it. This could be in the file itself, or maybe you just don’t like the commit message. You can update these with the git commit –amend command. First, let’s run git log to see that most recent commit.
We want to change the subheader to be an awesome subheader, and we also want to update the message for the commit. Here is how we can do that.
That most recent commit has now been amended or changed. Since we changed the HTML code and the commit message itself, the checksum is now different. In addition, we can see the commit just before this most recent one is the same. So we kind of clobbered or made an overwrite of the most recent commit.
Make Use Of .gitignore files
Once you have a git project, all files are tracked. In some cases, you might not want this. There may be a log file that changes continuously, and you don’t want to track that. Another example is something like a .ENV file that holds passwords for an application. Tracking that locally is no big deal, but if you decide to push your project to GitHub or some other remote repository, those secret passwords are not so secret anymore. To tell Git to ignore certain files or directories, you can use the .gitignore file. We can add a .gitignore file to the project root like so.
Some key .gitignore points:
- Lives in project/.gitignore
- Has a list of rules for what to ignore
- Changes to ignored files are ignored by Git
- Uses RegEx Pattern matching
- Can use negative expressions
- Can ignore entire directory with trailing slash
- Can use comments
- # This is a comment
Push Code To GitHub
The last and final thing we will do is to push our code to GitHub. Should your computer spontaneously combust between now and the next time you go to code, your project will be safe and sound on GitHub. To push your code to GitHub you’ll first need to have a GitHub account. Once you have an account, you can choose to make a new repository. It can be public or private.
As you create the repository, you need to name it and give it a description. For our purposes, we named this repository gitforbeginners with a description of Git For Beginners Tutorial. Based on this info, GitHub will provide you with the information you need to push code to the repository.
- git remote add origin https://github.com/username/gitforbeginners.git
- git push -u origin master
To push your code, navigate to the root of your project from the command line, and enter the commands above. Once the code push is complete, you’ll be able to see all the commits, changes, and edits you have been working on locally, right in the UI of GitHub.
All the commits that we have been working on via the command line, can now be viewed in the GitHub web interface. This is similar to when we use git log at the command log. Pretty cool!
You can also now click on any particular commit, and view the diff information very easily. This is the equivalent of using git diff at the command line.
Learning More About Git
- Git Docs Official Tutorial (git-scm.com)
- Resources to learn Git (try.github.io)
- Become a git guru (atlassian.com)
- Comprehensive Git Tutorial (vogella.com)
- Git For Absolutely Everyone (thenewstack.io)
- git – the simple guide (rogerdudler.github.io)
Git For Beginners Summary
In this tutorial, we had a nice crash course on how to use Git. Getting used to Git is a matter of kicking the tires and using it on your own projects. It’s a great way to manage your source code. Git helps us to track the changes to our files over time and to view or compare those changes as we like. Learning the basics of Git is also a great step to better collaboration since you can share projects via GitHub, or possibly contribute to an open-source project.