Have you ever made a change in a project and later wanted to undo it? Or you want to know “what has changed since a point in time?” Version control helps with both of these and more.
When I first started programming, I would zip up my entire project and have dated archives. While it did help with the “undo large changes” part, it didn’t solve the “what is changing between the versions” problem. A short time later, I was introduced to version control.
There are many different types of version control, Git, Subversion (svn), and Mercurial, to name a few. Let’s talk about Git. (It’s the same “git” from sites like github.com and gitlab.com)
Installing git
Git is available for Windows, macOS, and Linux.
NOTE: There are graphical tools for git
like Git GUI. However, it’s worth the effort to
learn git
on the command line.
Windows: If you haven’t installed the Windows Subsystem for Linux (WSL), the simplest option I’ve found so far is using Git BASH which is installed by Git for Windows.
macOS: git
is installed as part of the Command Line Tools for Xcode. These can
either be installed by installing Xcode or by downloading the command
line tools package directly.
Linux: There’s a good chance that git
is preinstalled on your machine. If not, it
can likely be installed via your preferred package manager.
New Terminology
-
Repository: Often shortened to “repo”. Contains your project in its current form and all its history.
-
Commit: Is used as both a concept and an action. A commit is a set of changes made in your project, similar to a point in time for your project. When you commit changes, you are adding them to the history of your project.
-
Branch: Made up of a series of commits. It’s kind of like a timeline within your repository.
┌─ Repository ────────────────────────┐
│ │
│ ┌── Branch │
│ │ │
│ ▼ │
│ main: ●─────●─────●─────●─────●───▸ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ └──── Commits ────┴─────┘ │
│ │
└─────────────────────────────────────┘
Create a New Repo
Time to create your first repo! While a repo can be created in any existing folder, it may be best to start with a new directory. That way, if anything goes wrong, nothing important is lost.
Feel free to make a new directory on your computer in your favorite manner. On the command line, I’ll run something like:
mkdir test-repo
cd test-repo
(test-repo
is the name of the new directory. The mkdir
command makes the
directory and cd
changes to the directory.)
Once your new directory is created and your command line is working in that directory, run:
git init
After running that command you should see something like:
$ git init
Initialized empty Git repository in ~/test-repo/.git/
You now have a new repo. By default, your branch will be either master
or main
(master
was the default for many years. In 2022, the default was changed to main
).
To see the status of your repo at any time, run git status
. This will output something
like:
$ git status
On branch main
No commits yet
nothing to commit (create/copy files and use "git add" to track)
Add Files
To add files to your repo, first, create a text file. This can be done in any way you like. A quick command line way is:
echo "hello world" > file.txt
What’s the status of the repo now? Running git status
will report something like:
$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
file.txt
nothing added to commit but untracked files present (use "git add" to track)
Notice the new “Untracked files” section that lists the “file.txt” file just created. When a file is “untracked” it means it’s in the same directory as your repo, but the repo isn’t watching it for changes. Since we want the repo to watch the file for changes, we need to add it to the repo.
To add a file to the repo, we can use the git add
command to add files to the repo. This
command requires a list of files. To add file.txt
we need to run:
git add file.txt
The git add
command most likely will not have any output, but if we then run git status
we can see the change. Altogether, this looks something like this:
$ git add file.txt
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: file.txt
Notice our file.txt
is now under “Changes to be committed”! Next up, let’s commit them.
Committing changes
When we commit changes, we add them to the history of our project. Commits allow us to see what’s changed in our project over time. Note that commits are not created automatically. It’s up to you the developer to create the commit. Typically, a single logical set of work makes a good commit.
A commit needs a message which describes the set of changes it contains. This message can be whatever you want. I usually write the message so it fits in the sentence “This commit will ${commit_message}”. For example, “This commit will create starting file.txt” thus a commit message of “create starting file.txt”.
To create a commit we use the git commit
command. This command takes the commit message
using the -m
flag. All together this will look like this:
git commit -m "create starting file.txt"
Running the command looks something like this:
$ git commit -m "create starting file.txt"
[main (root-commit) b82591d] create starting file.txt
1 file changed, 1 insertion(+)
create mode 100644 file.txt
Running git status
will show that our file is no longer untracked or ready to be
committed. Since there is now one commit, the “No commits yet” message is also gone.
$ git status
On branch main
nothing to commit, working tree clean
Since git
is about tracking changes and since the repo contains all the changes in the
folder, there is nothing special for the git status
command to report. Thus the
“nothing to commit, working tree clean” message.
Looking back in time
Before we look at the history of the repo, let’s create a few more changes. Go ahead and create a few more files, make changes to the existing one, and add a few commits. This is what I did:
$ echo "this is more text" > another-file.txt
$ git add another-file.txt
$ git commit -m "add another file"
[master 9eea9ed] add another file
1 file changed, 1 insertion(+)
create mode 100644 another-file.txt
$ echo "another line" >> file.txt # The ">>" here appends the content to file.txt
$ git add file.txt
$ git commit -m "add more text to file.txt"
[master e5f06c7] add more text to file.txt
1 file changed, 1 insertion(+)
$ echo "new text" > another-file.txt
$ git add another-file.txt
$ git commit -m "replace the text in another-file.txt"
[master 90451bc] replace the text in another-file.txt
1 file changed, 1 insertion(+), 1 deletion(-)
$ git status
On branch master
nothing to commit, working tree clean
The git log
command allows us to see what has changed in our project over time. Running
it provides a reverse order history of the commits:
$ git log
commit 90451bce664543b4a2a306fe04561b190c717cb3 (HEAD -> master)
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:55:44 2022 -0400
replace the text in another-file.txt
commit e5f06c79b7fe532fb35501e1f8261673f316f732
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:55:01 2022 -0400
add more text to file.txt
commit 9eea9edfce1b4ac16640e74af119a7bc1d385e3f
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:54:15 2022 -0400
add another file
commit b82591d9355573562bfbcba90ccd2b8ffe62583e
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:46:11 2022 -0400
create starting file.txt
To see how what changes have been made, the -p
flag (the “p” is for “patch”). Running
git log -p
results in:
commit 90451bce664543b4a2a306fe04561b190c717cb3
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:55:44 2022 -0400
replace the text in another-file.txt
diff --git a/another-file.txt b/another-file.txt
index 23446b3..eee417f 100644
--- a/another-file.txt
+++ b/another-file.txt
@@ -1 +1 @@
-this is more text
+new text
commit e5f06c79b7fe532fb35501e1f8261673f316f732
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:55:01 2022 -0400
add more text to file.txt
diff --git a/file.txt b/file.txt
index 3b18e51..fdff486 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
hello world
+another line
commit 9eea9edfce1b4ac16640e74af119a7bc1d385e3f
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:54:15 2022 -0400
add another file
diff --git a/another-file.txt b/another-file.txt
new file mode 100644
index 0000000..23446b3
--- /dev/null
+++ b/another-file.txt
@@ -0,0 +1 @@
+this is more text
commit b82591d9355573562bfbcba90ccd2b8ffe62583e
Author: Ethan Smith <examplegit@onebytegone.com>
Date: Sat Sep 10 14:46:11 2022 -0400
create starting file.txt
diff --git a/file.txt b/file.txt
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/file.txt
@@ -0,0 +1 @@
+hello world
Each commit now shows what changed in that commit, a diff (short for difference). The “+” plus sign indicates lines that have been added. The “-” minus sign indicates lines that have been removed. If a line was replaced, the old line will be marked as being removed and the new line will be marked as being added.
Conclusion
Congrats! You’ve taken the first step on the journey of version control. There is much more to discover and many useful ways of using the tool.
If you have questions on git feel free to ping me on Twitter. Don’t always have an answer, but am happy to try.
As a parting question, what other things could you put in version control? Is it limited to just code?